
import * as THREE from "three";
import * as TWEEN from '@tweenjs/tween.js'
import * as BufferGeometryUtils from 'three/examples/jsm/utils/BufferGeometryUtils'
import {SVGLoader} from "three/examples/jsm/loaders/SVGLoader";
import { CSS3DRenderer,CSS3DObject } from 'three/examples/jsm/renderers/CSS3DRenderer'
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls'
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader'
import { EffectComposer } from 'three/examples/jsm/postprocessing/EffectComposer'
import { RenderPass } from 'three/examples/jsm/postprocessing/RenderPass'
import { SMAAPass } from 'three/examples/jsm/postprocessing/SMAAPass'
import {loadSVG,UUIDUtil, CloneUtil,JSONUtil,AssistUtil} from '../../room/3d/ThreeAssist'
import { parkOption, parkClickData } from './ossData'

export namespace IDC{  


/**
 * 互通事件，自组件所处DOM容器抛出
 * */
export const MapEvent = {MOUSE_OVER:"map_mouseOver", MOUSE_OUT:"map_mouseOut", NODE_CLICK:"map_clickNode", CLICK_OUT:"map_clickOut", ONFOCUS:"onFocus"};
export const scenes = new THREE.Group();
// export let particleRenderer = new ParticleRenderer();
const SVGs = {}, GLB_MODELS = {};

    
export class HtmlMarker extends THREE.Object3D{
    marker: CSS3DObject;
    data: any;

    constructor(data,option, innerHTMLCont) {
        super();
        this.data = data;
        this.option = option;
        this.onClick = option.onClick;
        this.createMarker(innerHTMLCont);
        this.lookAtCam = option.lookAtCam || false;
    }

    setHtmlContent(innerHTMLCont){        
        this.container.innerHTML = innerHTMLCont;
        let d = this.container.lastChild;
        this.container.style.width = d.clientWidth + "px";
        this.container.style.height = d.clientHeight + "px";
        this.container.onpointerdown = this.onClick;
    }

    createContainer(){
        this.container = document.createElement("div");
        this.container.className = "htmlMark";
        this.container.option = this.option;
        this.container.data = this.data;
        return this.container;
    }

    createMarker(innerHTMLCont){
        this.container = this.createContainer();
        if (typeof this.option.tipUrl === 'function'){
            let content = this.option.tipUrl.call(this,this.data);
            if (typeof content === "string"){
                this.container.innerHTML = content;
            }else {
                this.container.append(content);
            }
        }else {
            this.setHtmlContent(innerHTMLCont);
        }
        this.marker = new CSS3DObject(this.container);
        this.marker.scale.multiplyScalar(0.1);
        this.add(this.marker);
    }

    destroy(){
        this.remove(this.marker);
        if (this.parent){
            this.parent.remove(this);
        }
    }

}

    
    
export class DigitalTwin {
    dom: HTMLElement;
    scene: THREE.Scene;
    needInit: boolean;
    ready: boolean; 
    parkOption: any;
    get actScene() {
        console.log(' 1039 t.World  get actScene ');
        return this._actScene
    }
    set actScene(t) {
        console.log(' 1038 actScene ');
        this._actScene = t
    }
    constructor(option = {}, data, dom3d, parkOption) {
        console.log(' 1037 t.World constructor ');
        this.dom = dom3d;
        this.parkOption = parkOption;
        this.option = {
            bgColor: "#121523",
                offsetX: 0,
                offsetY: 0,
                offsetZ: -20,
                mapHeight: 15,
                mapDepth: 50,
                mapWidth: 30,
                floorColor: "#113434",
                ignoreOpacity: .01,
                roomColor: "#272928",
                floorOpacity: .6,
                wallOpacity: .4,
                mapColor: "#00040b",
                mapOpacity: 1,
                mapBorderColor: "#2c8cb1",
                parkWidth: 320,
                parkHeight: 300,
                parkColor: "#2c8cb1",
                parkOpacity: "0.6",
                clickOut_enable: !0,
                wallColor: "#41fcc6",
                gap_y: 20,
                scale: .3,
                fontColor: "#fad72c",
                fontSize: 120,
                particleNum: 8e4,
                develop: !0,
                hudCard: !1,
                animateDur: 1e3,
                animateWait: 5e3,
                buildingOpacity: .2,
                groundColor: "#87e1d7",
                parkOffsetX: 0,
                parkOffsetY: 0,
                ambientLightColor: "#ffffff",
                ambientLightIntensity: 1,
                dirLightColor: "#ffffff",
                dirLightIntensity: 1,
                dirLightColor2: "#ffffff",
                dirLightIntensity2: .1,
                dirLightColor3: "#ffffff",
                dirLightIntensity3: 1,
                noInverted: !1,
                floorWallMap: void 0,
                svgsFile: void 0,
                envMap: void 0,
                targetCode: void 0,
                buildingLight: "#00ffff",
                floorWallTopColor: "#00ffff"
        };
        for (let k in option){
            this.option[k] = option[k];
        }
        this.option.world = this;
        // particleRenderer.option = this.option;
        this.nodes = [];
        this.init();
        if (data){
            this.setData(data);
        }
        // this.perf = new Stats();
        // document.body.append(this.perf.dom);
        this._actScene = null;
        this.mouseDownCard = null;
        this.addEventListeners();
    }
    
    getReady() {
        return this.ready;
    }
    init() {
        console.log(' 1036 init ');
      //场景
      this.scene = new THREE.Scene();
      this.scene.fog = new THREE.Fog(this.option.bgColor,1,1290);
      //摄像机
      this.camera = new THREE.PerspectiveCamera( 50, this.dom.clientWidth / this.dom.clientHeight, 0.1, 2500 );
      this.camera.position.set( this.option.offsetX, this.option.offsetY, this.option.offsetZ );
      //渲染器
      this.needRender = true;
      this.renderer = new THREE.WebGLRenderer( { antialias: true, alpha:true} );
      this.renderer.setPixelRatio( window.devicePixelRatio );
      this.renderer.setSize( this.dom.clientWidth, this.dom.clientHeight );
      this.renderer.shadowMap.enabled = true;
      this.renderer.outputEncoding = THREE.sRGBEncoding;
      this.renderer.toneMapping = THREE.CineonToneMapping;
      this.renderer.toneMappingExposure = 1;
      this.renderer.physicallyCorrectLights = true
      this.renderer.setClearColor("#000",0);
      this.renderer.logarithmicDepthBuffer = true;
      this.dom.appendChild( this.renderer.domElement );

      this.css3DRenderer = new CSS3DRenderer();
      this.css3DRenderer.setSize(this.dom.clientWidth, this.dom.clientHeight);
      this.css3DRenderer.domElement.style.position = "absolute";
      this.css3DRenderer.domElement.style.top = "0px";
      this.dom.appendChild( this.css3DRenderer.domElement );

      this.ambientLight = new THREE.AmbientLight( this.option.ambientLightColor, this.option.ambientLightIntensity);
      this.scene.add( this.ambientLight );

      this.dirLight = new THREE.DirectionalLight( this.option.dirLightColor,this.option.dirLightIntensity );
      this.dirLight.position.set( 150, 500, -150 );
      this.dirLight.castShadow = true;
      this.dirLight.visible = true;
      this.dirLight.shadow.mapSize.width = 5096;//16382;
      this.dirLight.shadow.mapSize.height = 5096;//16382;
      this.dirLight.shadow.camera.top = 1000;
      this.dirLight.shadow.camera.bottom = -1000;
      this.dirLight.shadow.camera.left = - 1000;
      this.dirLight.shadow.camera.right = 1000;
      this.dirLight.shadow.camera.near = 0.1;
      this.dirLight.shadow.camera.far = 1500;
      this.dirLight.shadow.bias = 0.0000000001;
      this.dirLight.shadow.needsUpdate = true;
      this.dirLight.shadowDarkness = 0.5;
      this.scene.add( this.dirLight );

      this.dirLight2 = new THREE.DirectionalLight( this.option.dirLightColor2,this.option.dirLightIntensity2 );
      this.dirLight2.position.set( -150, 500, -150 );
      this.dirLight2.castShadow = true;
      // this.dirLight2.onlyShadow = true;
      this.dirLight2.visible = true;
      this.dirLight2.shadow.mapSize.width = 5096;//16382;
      this.dirLight2.shadow.mapSize.height = 5096;//16382;
      this.dirLight2.shadow.camera.top = 1000;
      this.dirLight2.shadow.camera.bottom = -1000;
      this.dirLight2.shadow.camera.left = - 1000;
      this.dirLight2.shadow.camera.right = 1000;
      this.dirLight2.shadow.camera.near = 0.1;
      this.dirLight2.shadow.camera.far = 1500;
      this.dirLight2.shadow.bias = 0.0000000001;
      this.dirLight2.shadow.needsUpdate = true;
      this.dirLight2.shadowDarkness = 0.5;
      this.scene.add( this.dirLight2 );

      // this.dirLight3 = new THREE.DirectionalLight( this.option.dirLightColor3,this.option.dirLightIntensity3 );
      // this.dirLight3.position.set( 1000, 2000, -2000 );
      // this.dirLight3.visible = false;
      // this.dirLight3.castShadow = false;
      // this.scene.add( this.dirLight3 );

      //控制器
      this.controls = new OrbitControls(this.camera, this.dom);
      this.controls.screenSpacePanning = true;
      this.controls.enableRotate = this.option.develop;
      this.controls.enablePan = this.option.develop;
      this.controls.maxPolarAngle = Math.PI/2.2;
      this.renderer.domElement.removeAttribute("tabindex");

      // this.controls.target.set(this.parameters.offsetX,0,0);
      this.controls.update();

      this.composer = new EffectComposer(this.renderer);
      this.renderPass = new RenderPass( this.scene, this.camera );
      this.composer.addPass( this.renderPass );
      //抗锯齿
      const pass = new SMAAPass( window.innerWidth * this.renderer.getPixelRatio(), window.innerHeight * this.renderer.getPixelRatio() );
      this.composer.addPass( pass );
      // this.outlinePass = new OutlinePass( new Vector2( this.dom.clientWidth, this.dom.clientHeight ), this.scene, this.camera );
      // this.composer.addPass( this.outlinePass );

      // this.bizCardsSys = new CardsSpace(this);

      // if (this.option.develop){
      //     this.editor = new Editor(this);
      // }

      // this.sizeScale = document.createElement("object");
      // this.sizeScale.style.width = "100%";
      // this.sizeScale.style.height = "100%";
      // this.dom.append(this.sizeScale);
      // 尺寸适配监听
      // window.onresize = this.onWindowResize;
      // document.body.onresize = this.onWindowResize;
      if (this.option.hasOwnProperty("onlyServices") && this.option.onlyServices){
          this.needInit = true;
          this.ready = true;
        }
        
      this.renderer.setAnimationLoop(this.animate.bind(this));
    }
    render() {

        if (this.option.scale) {
            scenes.scale.multiplyScalar(this.option.scale);
        }
        scenes.rotateX(Math.PI/2);
        scenes.rotateY(Math.PI);
        this.scene.add(scenes);
            
        this.scene.scale.set(0.11, 0.11, 0.11)
       // this.scene.position.set(-5, 0, 10); 

        if (this.option.hasOwnProperty("onlyServices") && this.option.onlyServices){
           this.serviceScenes = new ServiceScenes(this.option,this.data);
        } else {
            this.markerClickEmulator2Park(parkClickData, parkOption);
            /**
             * Liam this.loadMap();
            
                if (this.option.targetCode){
                    this.redirect(this.option.targetCode);
                }else {
                    this.dispatchExternalEvent(new CustomEvent(MapEvent.ONFOCUS, {
                        detail: {
                            code: this.data.code,
                            type: "world"
                        }
                    }));
                }
             */
        }
        this.initAutoAnimate();
    }
    updateLoading(load,total,show = true){
        if (!show && this.loadingMask.parentNode){
            this.loadingMask.parentNode.removeChild(this.loadingMask);
        } else if(!this.loadingMask){
            this.loadingMask = document.createElement("div");
            this.loadingMask.className= "loading_mask";               
            
            const loadingBarBG = document.createElement("div");
            loadingBarBG.id = "loadingBarBG";
            const loading = document.createElement("div");
            loading.id = "loading";
            loadingBarBG.append(loading);
            const title = document.createElement("label");
            title.innerText = "素材加载中：";
            const loadNum = document.createElement("span");
            loadNum.id = "loadNum";
            const totalNum = document.createElement("span");
            totalNum.id = "totalNum";
            totalNum.innerText = "/"+total;
            this.loadingMask.append(loadingBarBG,title,loadNum,totalNum);
            this.dom.append(this.loadingMask);
            this.updateLoadingNum(load, total);
        }
    }
    updateLoadingNum(load, total) {
        try {
            document.getElementById("loadNum").innerText = load;
            if (total) {
                document.getElementById("loading").style.width = (load/total)*100 + "%";
            }
        } catch (ex) {
            ex = null;
        }
    }

    loadAllSvg() {
        console.log(' 1033 loadAllSvg ');
        this.svgUrls = [];
        this.svgCount = 0;
        this.dealData(this.data);
        let loader = new SVGLoader();
        let scope = this;
        if (this.option.svgsFile) {
            loadSVG(this.option.svgsFile, function (data) {
                for (const node of data.children[0].children) {
                    SVGs[node.id] = loader.parse(node.outerHTML);
                }
                // scope.needRender = true;
                scope.loadModels();
            });
        }else if(this.svgUrls){
            this.updateLoading(this.svgCount,this.svgUrls.length);
            for (let url of this.svgUrls){
                loader.load(url,data => {
                    SVGs[url] = data;
                    scope.svgCount++;
                    
                    this.updateLoadingNum(this.svgCount,this.svgUrls.length);
                    if (scope.svgCount === scope.svgUrls.length){
                        this.updateLoading(this.svgCount,this.svgUrls.length,false);
                        // scope.needRender = true;
                        this.loadModels();
                    }
                });
            }
        }
    }
    loadEnv() {
        console.log(' 1032 loadEnv ');
        this.option.envMap
    }
    /** 从数据中获取所有模型地址 */
    getModelUrls(data){
        console.log(' 1031 getModelUrls ');
        if (data.hasOwnProperty("model")){
            if (this.modelUrls.indexOf(data.model) === -1)
                this.modelUrls.push(data.model);
        }
        if (data.hasOwnProperty("nodes")){
            for (let n of data.nodes)
                this.getModelUrls(n);
        }
    }
    /** 缓存数据中的所有模型 */
    loadModels(){
        console.log(' 1030 loadModels ');
        this.modelUrls = [];
        let modelCount = 0;
        this.getModelUrls(this.data);
        if (this.modelUrls.length > 0) {
            this.updateLoading(modelCount, this.modelUrls.length);
        }else {
            this.needInit = true;
            this.ready = true;
            return;
        }
        const setShadow = (item) => {
            item.castShadow = true;
            // child.receiveShadow = true;
            if (item.name === "地板" || item.receiveShadow === "true"){
                item.castShadow = false;
                item.receiveShadow = true;
            }
            if (item.children){
                for (let m of item.children) {
                    setSceneShadow(m);
                }
            }
        }
        const setSceneShadow = (scene) => {
            if (Array.isArray(scene.children)){
                for (let child of scene.children){
                    setShadow(child)
                }
            }else {
                setShadow(scene.children)
            }

        }
        const loadModel = (url)=>{
            const loader = new GLTFLoader();
            const scope = this;
            loader.loadAsync(url).then(value => {
                setSceneShadow(value.scene);
                GLB_MODELS[url] = value.scene;//.scene
                modelCount ++;
                this.updateLoadingNum(modelCount, this.modelUrls.length);
                if (modelCount === this.modelUrls.length){
                    scope.needInit = true;
                    scope.ready = true;
                    this.updateLoading(modelCount,this.modelUrls.length,false);
                }
            });
        }
        for (const url of this.modelUrls){
            loadModel(url);
        }
    }

    /** 解析节点上的SVG路径 */
    dealData(node,level = 0){
        console.log(' 1029 dealData ');
        node.level = level;
        if (node.hasOwnProperty("svgSrc")){
            if (this.svgUrls.indexOf(node.svgSrc) === -1)
                this.svgUrls.push(node.svgSrc);
        }
        if (node.hasOwnProperty("groundSvg")){
            if (this.svgUrls.indexOf(node.groundSvg) === -1)
                this.svgUrls.push(node.groundSvg);
        }
        if (node.hasOwnProperty("nodes")){
            for (let n of node.nodes){
                this.dealData(n,level+1);
            }
        }
    }
    
    loadMap() {
        
        /** Liam
         *  暂时直接进入楼栋，不做地图页加载处理
        console.log(' 1028 loadMap ');
        this.map || (this.map = new World(this.option,this.data)),
        this.park && this.park.destroy(),
        this.map.loadMap(),
        this.nodes = this.map.nodes
         */
    }

    markerClickEmulator2Park(parkClickData, parkOption) {
        if (parkOption) {
            for (let k in parkOption){
                this.option[k] = parkOption[k];
            }
        }
        let e = parkClickData.code;
        this.option.world.loadPark(e),
        this.option.world.dispatchExternalEvent(new CustomEvent("map_clickNode",{
            detail: {
                userData: parkClickData
            }
        })),
        this.option.world.dispatchExternalEvent(new CustomEvent("onFocus",{
            detail: {
                code: e,
                type: "park"
            }
        }))
    }

    loadPark(code) {
        console.log(' 1027  loadPark ');
        for (let p of this.data.nodes){
            if (!p.type || p.type === "park"){
                if (p.code === code){
                    if (this.map){
                        this.map.hide();
                    }
                    if (this.park){
                        // if (this.park.name !== name)
                        this.park.destroy();
                    }  
                    this.park = new Park(this.option, p);
                    this.park.createPark();
                    this.nodes = this.park.buildings;
                }
            }else if (p.type === "service" && code === p.code){
                if (this.map){
                    this.map.hide();
                }
                if (this.park){
                    this.park.destroy();
                }
                if (this.serviceScenes){
                    this.serviceScenes.update(p);
                }else{
                    this.serviceScenes = new ServiceScenes(this.option,p);
                }
            }
        }
    }

    setData(t) {
        console.log(' 1026 setData ');
        t.type = "world",
        this.data = t,
        this.dataProcess()
    }
    dataProcess() {
        console.log(' 1025 dataProcess ');
        this.loadAllSvg(),
        this.loadEnv()
    }
    
    redirect(path){
        clearTimeout(this.renderTimer);
        this.needRender = true;
        this.path = path;
        const scenePath = this.getScenePath();
        let diffIndex = 0;
        for (let i = 0; i < this.path.length; i ++){
            if (scenePath[i] && this.path[i] === scenePath[i]){
                diffIndex = i+1;
            }
        }
        this.isRedirect = true;
        if (this.path.length === 0){           
           /** Liam this.loadMap();  */
        }
        if (diffIndex < 1 && this.path.length > 0 || (diffIndex === 1 && this.path.length === 1)) {
            this.loadPark(this.path[0]);
        }
        if ((diffIndex < 2 && this.path.length > 1) || (diffIndex === 2 && this.path.length === 2)){
            this.park.redirect(this.path[1]);
        }
        if (diffIndex < 3 && this.path.length > 2){
            setTimeout(()=>{
                this.dispatchExternalEvent(new CustomEvent(MapEvent.NODE_CLICK,{detail:{code:this.path[2],redirect:true}}));
            },3000-1500*diffIndex);
        }

        if (diffIndex < 4 && this.path.length > 3){
            setTimeout(()=>{
                this.dispatchExternalEvent(new CustomEvent(MapEvent.NODE_CLICK,{detail:{code:this.path[3],redirect:true}}));
            },3000-1500*diffIndex);
        }

        const types = ["world","park","building","floor","room"];
        let tarCode = this.path.length > 0 ? this.path[this.path.length - 1] : this.map.code;
        this.dispatchExternalEvent(new CustomEvent(MapEvent.ONFOCUS,{detail:{code:tarCode,type:types[this.path.length]}}));

        this.renderTimer = setTimeout(()=>{
            this.needRender = false;
        },10000);
    }
    
    getScenePath() {
        console.log(' 1022 getScenePath ');
        let path = [] as any[];
        const getPath = (scene)=>{
            path.unshift(scene.code);
            if (scene.parent){
                getPath(scene.parent);
            }
        }
        getPath(this.actScene);
        return path;
    }
    addEventListeners() {
        console.log(' 1021 idc_visual_base_v3 addEventListeners ');
        this.rayCaster = new THREE.Raycaster();
        this.mouseOverNode = null;
        this.focusNode = null;
        this.clientMousePosition = new THREE.Vector2();
        this.mousePosition = new THREE.Vector2();
        this.dom.addEventListener("mousemove", this.mouseMoveHandler.bind(this), false);
        this.dom.addEventListener("mousedown", this.mouseDownHandler.bind(this), false);
        this.dom.addEventListener("mouseup", this.mouseUpHandler.bind(this), false);
        this.dom.addEventListener("wheel", this.mouseWheelHandler.bind(this));
        this.dom.addEventListener("map_mouseOver", this.nodeMouseOver.bind(this), false);
        this.dom.addEventListener("map_mouseOut", this.nodeMouseOut.bind(this), false);
        this.dom.addEventListener("map_clickNode", this.nodeClick.bind(this),false);
        this.dom.addEventListener("map_clickOut", this.mapClickOut.bind(this),false);
        this.observeSize();
    }
    mouseWheelHandler(t) {
        console.log(' 1020 idc_visual_base_v3 mouseWheelHandler ', t);
        this.renderer.render(this.scene, this.camera);
        this.css3DRenderer.render(this.scene, this.camera);
    }
    mouseMoveHandler(event){
        //刷新摄像机到鼠标射线
        this.clientMousePosition.x = event.clientX - this.dom.offsetLeft - this.dom.offsetParent.offsetLeft;
        this.mousePosition.x  =  this.clientMousePosition.x / this.dom.clientWidth * 2 - 1;
        this.clientMousePosition.y = event.clientY - this.dom.offsetTop - this.dom.offsetParent.offsetTop;
        this.mousePosition.y = -this.clientMousePosition.y / this.dom.clientHeight * 2 + 1;
        this.rayCaster.setFromCamera(this.mousePosition,this.camera);
        this.clickable = false;
        const enableNodes = this.nodes.filter(node=>node.enable !== false);
        //射线拾取物体
        let intersects = this.rayCaster.intersectObjects(enableNodes,true);
        //未接触物体
        if(intersects.length < 1){
            if (this.mouseOverNode)//若存在mouseOver，抛出mouseOut
                this.dispatchExternalEvent(new CustomEvent(MapEvent.MOUSE_OUT,{detail:{nodeName:this.mouseOverNode.name,nodeCode:this.mouseOverNode.code, mouseX:this.mousePosition.x, mouseY:this.mousePosition.y}}));
            this.mouseOverNode = null;
            this.dom.style.cursor = "default";
            return;
        }
        let node = intersects[0].object;
        // for (let n of intersects){
        //     if (n.object.visible === true){
        //         node = n.object;
        //         break;
        //     }
        // }

        if (this.mouseOverNode !== node){
            if ( !node || (this.mouseOverNode && this.mouseOverNode.code !== node.code)){//若存在mouseOver，抛出mouseOut
                this.dispatchExternalEvent(new CustomEvent(MapEvent.MOUSE_OUT,{detail:{nodeName:this.mouseOverNode.name || null, nodeCode:this.mouseOverNode.userData.code, mouseX:this.mousePosition.x, mouseY:this.mousePosition.y}}));
                this.dom.style.cursor = "default";
            }
            if (node.userData.enable !== false)
            //over对象前后不一抛出over
                this.dispatchExternalEvent(new CustomEvent(MapEvent.MOUSE_OVER,{detail:{nodeName:node.name || node.userData.name, nodeCode:node.userData.code || node.code , mouseX:this.mousePosition.x, mouseY:this.mousePosition.y}}));
        }
    }

    nodeMouseOver(event){
        console.log(' 1018 idc_visual_base_v3 nodeMouseOver  ', event);
        this.mouseOverNode = this.getNodeByCode(event.detail.nodeCode);
        if (event.detail.nodeCode){
            this.dom.style.cursor = "pointer";
        }else {
            this.dom.style.cursor = "default";
        }
        // if (!this.mouseOverNode)return;
    }

    nodeMouseOut(event){
        console.log(' 1017 idc_visual_base_v3 nodeMouseOut  ', event);
        let node = this.mouseOverNode;
        if (event.detail.nodeName){
            node = this.getNodeByCode(event.detail.nodeCode) || this.mouseOverNode;
        }

        // node.material.color = new THREE.Color(this.getNodeColor(node.name));

        if (node === this.mouseDownNode){
            // this.mouseDownNode.position.z -= this.parameters.nodeClickOffset;
            this.mouseDownNode = null;
        }

        // this.outlinePass.selectedObjects = [];
    }

    mouseDownHandler(event){
        console.log(' 1016 idc_visual_base_v3 mouseDownHandler  ', event);
        let tweens = TWEEN.getAll();
        if (tweens.length > 0 || !this.controls.enabled)return;
        clearTimeout(this.renderTimer);
        this.needRender = true;
        this.clickable = true;
        console.log("cam:"+this.controls.object.position.x+","+this.controls.object.position.y+","+this.controls.object.position.z);
        console.log("tar:"+this.controls.target.x+","+this.controls.target.y+","+this.controls.target.z);
        if(!this.mouseOverNode)return;
        this.mouseDownNode = this.mouseOverNode;
        // this.mouseDownNode.position.z += this.parameters.nodeClickOffset;

        let intersect = this.rayCaster.intersectObject(this.mouseDownNode)[0];
        // console.log("node:" + this.mouseDownNode.name + ", x:" + intersect.point.x + ", z:" + intersect.point.z);
    }
    mouseUpHandler(event){
        console.log(' 1015 idc_visual_base_v3 mouseUpHandler  ', event);
        if (this.mouseDownCard){
            this.mouseDownCard = null;
            return;
        }
        // if (!this.clickable)return;
        // if (!this.option.develop)return;
        if((!this.mouseOverNode || !this.mouseDownNode) && this.option.clickOut_enable){
            this.dispatchExternalEvent(new CustomEvent(MapEvent.CLICK_OUT, {detail:{}}));
        }
        if (this.mouseDownNode && this.mouseDownNode === this.mouseOverNode){
            if (this.mouseDownNode.userData.enable !== false){
                this.isRedirect = false;
                this.dispatchExternalEvent(new CustomEvent(MapEvent.NODE_CLICK, {detail:this.mouseDownNode}));
                // this.mouseDownNode.position.z -= this.parameters.nodeClickOffset;
            }
        }
        this.mouseDownNode = null;
        this.renderTimer = setTimeout(()=>{
            this.needRender = !this.autoAnimate_pause;
        },3000)
    }
    
    nodeClick(t) {
        console.log(' 1014 idc_visual_base_v3 nodeClick  ', t);
        this.focusNode = this.getNodeByCode(t.detail.nodeCode) || this.mouseDownNode
    }
    mapClickOut(t) {
        console.log(' 1013 idc_visual_base_v3 mapClickOut  ', t);
        this.focusNode,
        this.focusNode = null
    }
    focus2Node(t) {
        console.log(' 1012 ');
    }
    getNodeByCode(code) {
        console.log(' 1011 getNodeByCode ', code);
        for(let node of this.nodes){
            if (node.code === "") continue;
            if (node.code === code) return node;
            for(let n of node.children){
                if (n.code === "") continue;
                if (n.code === code) return n;
            }
        }
    }
    observeSize() {
        console.log(' 1010 idc_visual_base_v3 observeSize  ');
        
        // 检查浏览器是否支持ResizeObserver
        if ('ResizeObserver' in window) {
            // 创建一个新的ResizeObserver实例
            const observer = new ResizeObserver(entries => {
                // entries数组包含了所有尺寸发生变化的元素
                for (const entry of entries) {
                    // 如果当前处理的元素就是我们想要监听的元素
                    if (entry.target === this.dom) {
                        // 获取元素的新尺寸
                        const newSize = {
                            width: entry.contentRect.width,
                            height: entry.contentRect.height
                        };
                        this.camera.aspect = this.dom.clientWidth / this.dom.clientHeight;
                        this.camera.updateProjectionMatrix();
                        this.renderer.setSize(this.dom.clientWidth, this.dom.clientHeight);
                        this.css3DRenderer.setSize(this.dom.clientWidth, this.dom.clientHeight);
                        this.needRender = true;
                    }
                }
            });
            observer.observe(this.dom);
        }
    }
    dispatchExternalEvent(t) {
        console.log(' 1009 idc_visual_base_v3 world dispatchExternalEvent  ', t);
        try{
            console.log(' 1009 idc_visual_base_v3 world dispatchExternalEvent JSON  ', JSON.stringify(t) );
        }catch(exx){
            console.log(exx);
        }
        this.dom.dispatchEvent(t)
    }
    dispatchFocusEvent(t) {
        console.log(' 1008 idc_visual_base_v3 world dispatchFocusEvent  ', t);
        if (this.isRedirect)
            return;
        this.eventTimer = setTimeout(( () => {
            this.dispatchExternalEvent(t),
            this.eventTimer = null
        }
        ), 0)
    }
    
    moveCamera(camPos,camTar,dur){
        new TWEEN.Tween(this.camera.position).to({x:camPos[0],y:camPos[1],z:camPos[2]},dur)
            // .onUpdate(()=>{
            //     this.controls.target.set(camTar[0],camTar[1],camTar[2]);
            //     this.controls.update();
            // })
            .start();
        new TWEEN.Tween(this.controls.target).to({x:camTar[0],y:camTar[1],z:camTar[2]},dur)
            .onUpdate(()=>{
                // this.controls.target.set(camTar[0],camTar[1],camTar[2]);
                this.controls.update();
            })
            .start();
    }

    // Function to get 2D screen coordinates of a 3D object using three.js
    getScreenCoordinates(obj) {
        // Create a vector with the object's position
        const vector = new THREE.Vector3();
        vector.setFromMatrixPosition(obj.matrixWorld);

        // Project the vector onto the camera's view
        const widthHalf = 0.5 * this.renderer.getContext().canvas.width;
        const heightHalf = 0.5 * this.renderer.getContext().canvas.height;
        vector.project(this.camera);

        // Convert the projected vector to 2D screen coordinates
        const x = (vector.x * widthHalf) + widthHalf;
        const y = -(vector.y * heightHalf) + heightHalf;

        // Return the screen coordinates as an object
        return { x, y };
    }

    
    initAutoAnimate() {
        console.log(' 1005 ');
        this.autoAnimate_config = this.data.animateConfig || this.option.animateConfig;
        this.autoAnimate_pause = true;
        if (!this.autoAnimate_config)return;
        this.autoAnimate_curStep = 0;
        this.autoAnimate_dur = this.option.animateDur;
        this.autoAnimate_wait = this.option.animateWait;
        // this.dom.addEventListener(MapEvent.MOUSE_OVER,(e)=>{this.pauseAutoAnimate();});
        // this.dom.addEventListener(MapEvent.MOUSE_OUT,(e)=>{this.initAutoAnimate();});
    }
    getAutoAnimateConfig() {
        console.log(' 1004 ');
        if (this.autoAnimate_config && this.autoAnimate_config.hasOwnProperty(this.actScene.code)){
            return this.autoAnimate_config[this.actScene.code];
        }
        return null;
    }

    playAutoAnimate() {
        console.log(' 1003 ');
        if (this.autoAnimate_pause)return;
        if (!this.autoAnimate_config)return;
        // this.path = this.getScenePath();
        if (this.autoAnimate_config.path && this.autoAnimate_config.path.length > 0){
            const sc = this.autoAnimate_config.path[this.autoAnimate_curStep];
            // const nextItem = this.actScene.children.filter(c => c.code === sc.code)[0];
            // const camPos = nextItem.data.camPos,tarPos = nextItem.data.camTar;
            // this.moveCamera(camPos,tarPos,this.autoAnimate_dur);
            // this.dispatchExternalEvent(new CustomEvent(MapEvent.NODE_CLICK,{detail:{code:sc.code,redirect:true}}));
            this.park.redirect(sc.code);
            this.autoAnimate_curStep = this.autoAnimate_curStep < this.autoAnimate_config.path.length-1 ? this.autoAnimate_curStep + 1 : 0;
            setTimeout(()=>{
                this.playAutoAnimate();
            },this.autoAnimate_wait);
        }
    }
    pauseAutoAnimate(pause=!0) {
        console.log(' 1002 ');
        this.autoAnimate_pause = pause;
        // this.path = this.getScenePath();
        this.playAutoAnimate();
    }
    animate(time) {
        if (this.needInit){
            this.render();
            this.needInit = false;
        }

        if (this.needRender){
            this.renderer.render(this.scene, this.camera);
            this.css3DRenderer.render(this.scene, this.camera);
        }

        if (!this.autoAnimate_pause){
            this.controls.autoRotate = true;
            this.controls.update();
        }

        TWEEN.update();
    }
}

    
    
    





    
    export class BaseScenes {
        data: any;
        option: any;

        constructor(option, data) {
            this.option = option;
            this.data = data;
            this.name = data.name;
            this.parent = data.parent;
            this.instance =new THREE.Group();
            // this.instance.scale.set(.01,.01,.01);
            this.instance.name = this.name;
            this.instance.code = this.data.code;
            this.code = this.data.code;
            this.nodes = [];
            this.children = [];
            this.effectNodes = [];
            this.billBoards = [];
        }
    
        computeRange(points){
            this.mapMaxPoint =new  THREE.Vector2(points[0],points[1]);
            this.mapMinPoint =new  THREE.Vector2(points[0],points[1]);
            for (let i = 0; i < points.length / 3; i ++){
                let x = points[ i * 3 ];
                let y = points[ i * 3 + 1 ];
                if (x > this.mapMaxPoint.x) this.mapMaxPoint.x = x ;
                if (x < this.mapMinPoint.x) this.mapMinPoint.x = x ;
                if (y > this.mapMaxPoint.y) this.mapMaxPoint.y = y ;
                if (y < this.mapMinPoint.y) this.mapMinPoint.y = y ;
            }
            this.width = this.mapMaxPoint.x - this.mapMinPoint.x;
            this.depth = this.mapMaxPoint.y - this.mapMinPoint.y;
        }
    
    
        show(opacity = 0.6){
            for (let child of this.effectNodes){
                if (child.material){
                    child.material.opacity = 0;
                    new TWEEN.Tween(child.material).to({opacity:0.1},50)
                        .onComplete(()=>{
                            child.material.depthWrite = true;
                            child.material.opacity = opacity;
                        })
                        .repeat(Math.random()*10/2)
                        .repeatDelay(Math.random()*100)
                        .delay(600)
                        .onStart(()=>{this.visible = true;})
                        .start();
                }
            }
        }
    
        setOpacity(opacity){
            const doSet = (o)=>{
                o.children.forEach((m)=>{
                    m.material.opacity = opacity;
                    if (m.hasOwnProperty("children") && m.children.length > 0)
                        doSet(m);
                })
            }
            doSet(this.instance);
        }
    
        hide(){
            for (let child of this.effectNodes){
                if (child.material){
                    child.material.opacity = 0;
                }
            }
            this.visible = false;
        }
    
        removeAll(){
            for (let ns of this.children){
                if (ns.instance.children.length>0){
                    ns.removeAll();
                    ns = null;
                }
            }
            let cs = [];
            cs.push(...this.instance.children);
            for (let c of cs){
                if (c instanceof THREE.Mesh) {
                    c.geometry.dispose();
                    if (c.material) {
                        if (c.material instanceof Array) {
                            for (let cm of c.material) {
                                cm.dispose();
                            }
                        } else {
                            c.material.dispose();
                        }
                    }
                }
                this.instance.remove(c);
                this.removeListeners();
                this.removeTip();
            }
            this.children = [];
            for (let k in this.overLaies){
                this.removeOverlay(k);
            }
            this.overLaies = {};
        }
    
        removeListeners(){
    
        }
    
        updateBillBoard(){
            if (this.billBoards.length > 0){
                for (let board of this.billBoards){
                    if (board.lookAtCam)
                        board.lookAt(this.option.world.camera.position.x,this.option.world.camera.position.y,this.option.world.camera.position.z);
                }
            }
        }
    
    
        createPlanes(){
            if (this.data.hasOwnProperty("tipUrl")){
                this.createTip();
            }
        }
    
        addOverlay_html(data,option){
            if (!this.overLaies){
                this.overLaies = {};
            }
            if (this.overLaies[option.code]){
                return;
            }
            let overlay =  new HtmlMarker(data,option);
            overlay.marker.element.style.width = option.width;
            overlay.marker.element.style.height = option.height;
            overlay.position.set(option.x,option.y,option.z);
            this.overLaies[option.code] = overlay;
            this.instance.add(overlay);
        }
    
        addOverlay_svg(data){
            if (!this.overLaies){
                this.overLaies = {};
            }
            let overlay = null;
            if (SVGs[data.url]){
                overlay = this.drawOverlay_svg(SVGs[data.url].paths);
                this.overLaies[data.url] = overlay;
                this.instance.add(overlay);
            }else {
                let loader = new SVGLoader();
                loader.load(data.url,data1 => {
                   overlay = this.drawOverlay_svg(data1.paths);
                    this.overLaies[data.url] = overlay;
                    this.instance.add(overlay);
                });
            }
        }
    
        drawOverlay_svg(paths){
            let overlay = THREE.Group();
            for (let path of paths){
                let fillColor = path.userData.style.fill;
                let mat = THREE.MeshBasicMaterial({
                   color:fillColor,
                });
                let geo = THREE.BufferGeometry();
                let shapes = path.toShapes( true );
                for (let shape of shapes){
                    let shapeGeo = THREE.ShapeGeometry(shape);
                    BufferGeometryUtils.mergeGeometries([geo,shapeGeo]);
                }
                let shapeMesh = THREE.Mesh(geo,mat);
                overlay.add(shapeMesh);
            }
            return overlay;
        }
    
        removeOverlay(code){
            if (!this.overLaies)return;
            let overlay = this.overLaies[code];
            if (!overlay)return;
            overlay.destroy();
            delete this.overLaies[code];
        }
    
        createTip(data, option = {}) {
            if (!data) {
                data =  this.data;
            }
            console.log('createTip data tipUrl', data.tipUrl, data)
            if (data.hasOwnProperty("tipUrl")){
                option.tipUrl = data.tipUrl;
                let billBoard = new HtmlMarker(data,option);
                // marker.rotateY(Math.PI / 8);
                // this.instance.getWorldPosition(billBoard.position);
                billBoard.position.set(0,0,-(this.data.height/2));
                this.instance.add(billBoard);
                if (this.parent)
                    this.parent.billBoards.push(billBoard);
                else
                    this.billBoards.push(billBoard);
                return billBoard;
            }
        }
    
        removeTip(){
            for (let b of this.billBoards){
                b.destroy();
            }
            this.billBoards = [];
        }
    
        createWall(path,height,weight,mat){
            let extrudeSettings = {
                steps: 200,
                bevelEnabled: true,
                bevelThickness: 100,
                extrudePath: path,
            };
            let pts = [];
            pts.push(new THREE.Vector2(0, -0.5 * weight));
            pts.push(new THREE.Vector2(-height, -0.5 * weight));
            pts.push(new THREE.Vector2(-height, 0.5 * weight));
            pts.push(new THREE.Vector2(0, 0.5 * weight));
    
            let shape = new THREE.Shape(pts);
            let geometry = new THREE.ExtrudeBufferGeometry(shape, extrudeSettings);
            return THREE.Mesh(geometry, mat);
        }
    
        loadGlbModel(url,then){
            const loader = new GLTFLoader();
            loader.loadAsync( url ).then(value => {
                if (typeof then === 'function'){
                    then.call(this, value);
                }
            });
        }
    
        /**
         * 创建倒影材质
         * option =>
         * params height color opacity speed
         * **/
        createInvertedMat (height = 10, color = "#00ffff", opacity = 0.5, speed = 1){
            // 顶点着色器
            const vertexShader = `
              uniform vec3 u_color;
              uniform float time;
              uniform float u_height;
              varying float v_opacity;
    
              void main() {
                  vec3 vPosition = position;
                  vPosition.z = position.z * 0.99;
                  v_opacity = mix(1.0, 0.0, vPosition.z / u_height * 1.0) * (1.0 + sin(time) * 0.5);
                  gl_Position = projectionMatrix * modelViewMatrix * vec4(vPosition, 1);
              }
           `;
            // 片元着色器
            const fragmentShader = `
              uniform vec3 u_color;
              uniform float u_opacity;
              varying float v_opacity;
              void main() {
                  gl_FragColor = vec4(u_color, v_opacity * u_opacity);
              }
            `;
    
            return new THREE.ShaderMaterial({
                uniforms: {
                    u_height: {
                        value: height,
                    },
                    u_opacity: {
                        value: opacity,
                    },
                    u_color: {
                        value: new THREE.Color(color),
                    },
                    time: {
                        value: 0,
                    },
                    speed: {
                        value: speed,
                    },
                },
                transparent: true,
                depthWrite: false,
                depthTest: false,
                side: THREE.FrontSide,
                vertexShader: vertexShader,
                fragmentShader: fragmentShader,
            });
        };
    
        createParkMat (map){
            const Shader = {
                vertexShader: ` 
                    varying vec3 vp;
                    void main(){
                    vp = position; 
                    gl_Position	= projectionMatrix * modelViewMatrix * vec4(position, 1.0);
                    }
                `,
                fragmentShader: `
                    varying vec3 vp;
                    uniform vec3 u_color;
                    uniform vec3 u_tcolor;
                    uniform float u_r;
                    uniform float u_length;
                    uniform float u_max;
                    float getLeng(float x, float y){
                        return  sqrt((x-0.0)*(x-0.0)+(y-0.0)*(y-0.0));
                    }
                    void main(){ 
                        float uOpacity = 0.3; 
                        vec3 vColor = u_color;
                        float uLength = getLeng(vp.x,vp.y);
                        if ( uLength <= u_r && uLength > u_r - u_length ) { 
                            float op = sin( (u_r - uLength) / u_length ) * 0.2 ;
                            uOpacity = op; 
                            if( vp.y<0.0){
                                vColor  = u_tcolor * 0.6; 
                            }else{ 
                                vColor = u_tcolor;
                            };
                        } 
                        gl_FragColor = vec4(vColor,uOpacity);
                    }
                `
            }
    
           this.parkMat = new THREE.ShaderMaterial({
                vertexShader: Shader.vertexShader,
                fragmentShader: Shader.fragmentShader,
                side: THREE.DoubleSide,
                uniforms: {
                   u_color: { value: new THREE.Color("#8dccd3") },
                   u_tcolor: { value: new THREE.Color("#34eab3") },
                   u_r: { value: 0.25 },
                   u_length: { value: 20 },//扫过区域
                   u_max: { value: 300 }//扫过最大值
                },
                transparent: true,
                depthWrite: false,
                map:map
            });
    
            return this.parkMat;
        }
    
        /**
         * 创建楼体材质
         **/
        createBuildingWallMat (height = 10, color = "#00ffff", opacity = 0.5, bank = 3.0){
            // 顶点着色器
            const vertexShader = `
              uniform float time;
              uniform float u_height;
              varying float v_opacity;
              varying vec3 vPosition;
    
              void main() {
                  vPosition = position;
                  v_opacity = mix(1.0, 0.0, (0.5 - position.z / u_height) * 1.0);
                  gl_Position = projectionMatrix * modelViewMatrix * vec4(vPosition, 1.0);
              }
           `;
            // 片元着色器
            const fragmentShader = `
              uniform float time;
              uniform float bank;
              uniform vec3 color;
              uniform float u_height;
              varying vec3 vPosition;
              uniform float u_opacity;
              varying float v_opacity;
              void main() {
                  float op = 0.0;
                  if(vPosition.z > time){
                       op = 1.0 - (vPosition.z - time) / (u_height / bank);
                  }
                  if(vPosition.z < time){
                       op = 1.0 - (time - vPosition.z) / (u_height / bank);
                  }
                  if(vPosition.z > (time + u_height / bank)){
                       op = 0.0;
                  }
                  if(vPosition.z < (time - u_height / bank)){
                       op = 0.0;
                  }
                  
                  gl_FragColor = vec4(color, u_opacity * op);
              }
            `;
    
            return [
                new THREE.MeshBasicMaterial({
                    transparent:true,
                    color:"#d0d0d0",
                    opacity:0.2,
                    visible:false
                }),
                new THREE.ShaderMaterial({
                uniforms: THREE.UniformsUtils.merge([
                    THREE.ShaderLib['phong'].uniforms,
                    {
                        u_height: {
                            value: height,
                        },
                        u_opacity: {
                            value: opacity,
                        },
                        color: {
                            value: new THREE.Color(color),
                        },
                        time: {
                            value: 0,
                        },
                        bank: {
                            value: bank,
                        },
                    }]),
                transparent: true,
                depthWrite: true,
                depthTest: true,
                side: THREE.FrontSide,
                lights:true,
                vertexShader: vertexShader,
                fragmentShader: fragmentShader,
                    needsUpdate:true
            })];
        };
    
        createBuildingLightMat (height = 10, color = "#00ffff", opacity = 0.5, speed = 1){
            // 顶点着色器
            const vertexShader = `
              uniform float time;
              uniform float u_height;
              varying float v_opacity;
              varying vec3 vPosition;
    
              void main() {
                  vPosition = position;
                  v_opacity = mix(1.0, 0.0, (0.5 - position.z / u_height) * 1.0);
                  gl_Position = projectionMatrix * modelViewMatrix * vec4(vPosition, 1.0);
              }
           `;
            // 片元着色器
            const fragmentShader = `
              uniform float time;
              uniform vec3 color;
              uniform float u_height;
              varying vec3 vPosition;
              uniform float u_opacity;
              varying float v_opacity;
              void main() {
                  float op = 0.0;
                  float lightWidth = 1.0;
                  if(vPosition.z > time){
                       op = 1.0 - (vPosition.z - time) / lightWidth;
                  }
                  if(vPosition.z < time){
                       op = 1.0 - (time - vPosition.z) / lightWidth;
                  }
                  if(vPosition.z > (time + lightWidth)){
                       op = 0.0;
                  }
                  if(vPosition.z < (time - lightWidth)){
                       op = 0.0;
                  }
                  gl_FragColor = vec4(color, u_opacity * op);
              }
            `;
    
            return [
                new THREE.MeshBasicMaterial({
                    transparent:true,
                    opacity:0,
                    visible:false
                }),
                new THREE.ShaderMaterial({
                    uniforms: THREE.UniformsUtils.merge([
                        THREE.ShaderLib['phong'].uniforms,
                        {
                            u_height: {
                                value: height,
                            },
                            u_opacity: {
                                value: opacity,
                            },
                            color: {
                                value: new THREE.Color(color),
                            },
                            time: {
                                value: 0,
                            },
                            speed: {
                                value: speed,
                            },
                        }]),
                    transparent: true,
                    depthWrite: false,
                    depthTest: true,
                    side: THREE.DoubleSide,
                    lights:true,
                    vertexShader: vertexShader,
                    fragmentShader: fragmentShader,
                    needsUpdate:true
                })];
        };
    
        /**
         * 创建楼层外墙材质
         **/
        createFloorWallMat (height = 3, color = "#00ffff", opacity = 1.0, speed = 1.0){
            // 顶点着色器
            const vertexShader = `
              uniform vec3 color;
              uniform float time;
              uniform float height;
              uniform float opacity;
              varying float v_opacity;
                
              void main() {
                  vec3 vPosition = position;
                  v_opacity = opacity;
                  gl_Position = projectionMatrix * modelViewMatrix * vec4(vPosition, 1.0);
              }
           `;
            // 片元着色器
            const fragmentShader = `
              uniform vec3 color;
              // uniform float opacity;
              varying float v_opacity;
              
              void main() {
                  gl_FragColor = vec4(color, v_opacity);
              }
            `;
    
            return new THREE.ShaderMaterial({
                uniforms: {
                    height: {
                        value: height,
                    },
                    opacity: {
                        value: opacity,
                    },
                    color: {
                        value: new THREE.Color(color),
                    },
                    time: {
                        value: 0,
                    },
                    speed: {
                        value: speed,
                    },
                },
                transparent: true,
                depthWrite: false,
                depthTest: true,
                side: THREE.DoubleSide,
                vertexShader: vertexShader,
                fragmentShader: fragmentShader,
            });
        }
    
        computeVectors(geometry){
            const _vector = THREE.Vector3();
            const computeVertexNormals = (positionAttribute, index)=>{
                if ( positionAttribute !== undefined ) {
    
                    let normalAttribute = THREE.BufferAttribute( new Float32Array( positionAttribute.count * 3 ), 3 );
    
                    const pA = THREE.Vector3(), pB = THREE.Vector3(), pC = THREE.Vector3();
                    const nA = THREE.Vector3(), nB = THREE.Vector3(), nC = THREE.Vector3();
                    const cb = THREE.Vector3(), ab = THREE.Vector3();
    
                    // indexed elements
    
                    if ( index ) {
    
                        for ( let i = 0, il = index.count; i < il; i += 3 ) {
    
                            const vA = index.getX( i + 0 );
                            const vB = index.getX( i + 1 );
                            const vC = index.getX( i + 2 );
    
                            pA.fromBufferAttribute( positionAttribute, vA );
                            pB.fromBufferAttribute( positionAttribute, vB );
                            pC.fromBufferAttribute( positionAttribute, vC );
    
                            cb.subVectors( pC, pB );
                            ab.subVectors( pA, pB );
                            cb.cross( ab );
    
                            nA.fromBufferAttribute( normalAttribute, vA );
                            nB.fromBufferAttribute( normalAttribute, vB );
                            nC.fromBufferAttribute( normalAttribute, vC );
    
                            nA.add( cb );
                            nB.add( cb );
                            nC.add( cb );
    
                            normalAttribute.setXYZ( vA, nA.x, nA.y, nA.z );
                            normalAttribute.setXYZ( vB, nB.x, nB.y, nB.z );
                            normalAttribute.setXYZ( vC, nC.x, nC.y, nC.z );
    
                        }
    
                    } else {
    
                        // non-indexed elements (unconnected triangle soup)
    
                        for ( let i = 0, il = positionAttribute.count; i < il; i += 3 ) {
    
                            pA.fromBufferAttribute( positionAttribute, i + 0 );
                            pB.fromBufferAttribute( positionAttribute, i + 1 );
                            pC.fromBufferAttribute( positionAttribute, i + 2 );
    
                            cb.subVectors( pC, pB );
                            ab.subVectors( pA, pB );
                            cb.cross( ab );
    
                            normalAttribute.setXYZ( i + 0, cb.x, cb.y, cb.z );
                            normalAttribute.setXYZ( i + 1, cb.x, cb.y, cb.z );
                            normalAttribute.setXYZ( i + 2, cb.x, cb.y, cb.z );
    
                        }
    
                    }
                    for ( let i = 0, il = normalAttribute.count; i < il; i ++ ) {
    
                        _vector.fromBufferAttribute( normalAttribute, i );
    
                        _vector.normalize();
    
                        normalAttribute.setXYZ( i, _vector.x, _vector.y, _vector.z );
    
                    }
                    normalAttribute.needsUpdate = true;
                    return normalAttribute;
                }
            };
    
            let positions = geometry.attributes.position.array;
            const normal = computeVertexNormals(geometry.attributes.position, geometry.index);
            let normals = normal.array;
            // Build new attribute storing barycentric coordinates
            // for each vertex
            let centers = THREE.BufferAttribute(new Float32Array(positions.length), 3);
            // start with all edges disabled
            for (let f = 0; f < positions.length; f++) { centers.array[f] = 1; }
            geometry.setAttribute( 'center', centers );
            // Hash all the edges and remember which face they're associated with
            // (Adapted from THREE.EdgesHelper)
            function sortFunction ( a, b ) {
                if (a[0] - b[0] != 0) {
                    return (a[0] - b[0]);
                } else if (a[1] - b[1] != 0) {
                    return (a[1] - b[1]);
                } else {
                    return (a[2] - b[2]);
                }
            }
            let edge = [ 0, 0 ];
            let hash = {};
            let face;
            let numEdges = 0;
    
            for (let i = 0; i < positions.length/9; i++) {
                let a = i * 9
                face = [ [ positions[a+0], positions[a+1], positions[a+2] ] ,
                    [ positions[a+3], positions[a+4], positions[a+5] ] ,
                    [ positions[a+6], positions[a+7], positions[a+8] ] ];
                for (let j = 0; j < 3; j++) {
                    let k = (j + 1) % 3;
                    let b = j * 3;
                    let c = k * 3;
                    edge[ 0 ] = face[ j ];
                    edge[ 1 ] = face[ k ];
                    edge.sort( sortFunction );
                    const key = edge[0] + ' | ' + edge[1];
                    if ( hash[ key ] == undefined ) {
                        hash[ key ] = {
                            face1: a,
                            face1vert1: a + b,
                            face1vert2: a + c,
                            face2: undefined,
                            face2vert1: undefined,
                            face2vert2: undefined
                        };
                        numEdges++;
                    } else {
                        hash[ key ].face2 = a;
                        hash[ key ].face2vert1 = a + b;
                        hash[ key ].face2vert2 = a + c;
                    }
                }
            }
    
            let index = 0;
            for (let key in hash) {
                const h = hash[key];
    
                // ditch any edges that are bordered by two coplanar faces
                let normal1, normal2;
                if ( h.face2 !== undefined ) {
                    normal1 = THREE.Vector3(normals[h.face1+0], normals[h.face1+1], normals[h.face1+2]);
                    normal2 = THREE.Vector3(normals[h.face2+0], normals[h.face2+1], normals[h.face2+2]);
                    if ( normal1.dot( normal2 ) >= 0.9999 ) { continue; }
                }
    
                // mark edge vertices as such by altering barycentric coordinates
                let otherVert;
                otherVert = 3 - (h.face1vert1 / 3) % 3 - (h.face1vert2 / 3) % 3;
                centers.array[h.face1vert1 + otherVert] = 0;
                centers.array[h.face1vert2 + otherVert] = 0;
    
                otherVert = 3 - (h.face2vert1 / 3) % 3 - (h.face2vert2 / 3) % 3;
                centers.array[h.face2vert1 + otherVert] = 0;
                centers.array[h.face2vert2 + otherVert] = 0;
            }
        }
    }
    

    export class Park extends BaseScenes{
        grounds: Array<any>;
        buildings: Array<any>;

        constructor(option, data) {
            data.type = "park";
            super(option, data);
            this.grounds = [];
            this.buildings = [];
        }
    
        createPark() {
             // if (!particleRenderer.particlePositions){
            //     particleRenderer.createPoints();
            // }
            // particleRenderer.particle_node.position.copy(this.instance.position);
            this.particleInView = 0;
            // if (this.data.svgSrc)
            //     this.loadGround(SVGs[this.data.svgSrc]);
            // if (this.data.imgMap)
            this.loadGround();
            this.loadBuildings();
            scenes.add(this.instance);
            if (this.data.callBack){
                this.data.callBack.call(this,this);
            }
            // this.option.world.playAutoAnimate();
            this.option.world.dom.addEventListener(MapEvent.ONFOCUS,(evt=>{
                if (this.ground){
                    this.ground.visible = evt.detail.type === "park";
                    // this.ground.material.opacity = evt.detail.type === "park" ? this.option.worldOpacity : this.option.worldOpacity/10;
                    // if (this.model)
                }
            }));
        }
        
        getBuildingData(code){
            for (let d of this.data.nodes){
                if (d.code === code){
                    return d;
                }
            }
        }
    
        loadBuildings() {
            for (let d of this.data.nodes){
                d.parent = this;
                let building = new Building(this.option, d);
                this.instance.add(building.instance);
                this.buildings.push(building.instance);
                this.children.push(building);
            }
            this.nodes = this.buildings;
        }
    
        loadGround(){
            const loadMap = (url)=>{
                let map = new THREE.TextureLoader().load(url);
                map.anisotropy = 64;
                return map;
            }
            if (this.data.model) {
                this.ground = GLB_MODELS[this.data.model];
                this.ground.rotation.set(Math.PI/2,0,0);
                if (this.data.modelScale) {
                    this.ground.scale.set(this.data.modelScale, this.data.modelScale, this.data.modelScale);
                }
                if (this.data.modelOffset) {
                    this.ground.position.set(this.data.modelOffset[0], this.data.modelOffset[1], 0);
                }
            } else {                
                this.ground = new THREE.Mesh(
                    new THREE.PlaneGeometry(this.option.worldWidth, this.option.worldHeight, 1, 1),
                    new THREE.MeshStandardMaterial({
                        color: new THREE.Color(this.option.worldColor),
                        roughness: 0.55,
                        // map: loadMap(this.data.imgMap),
                        transparent: true,
                        // normalMap: loadMap(this.data.normalMap),
                        // needsUpdate:true
                        // bumpMap: loadMap(this.data.bumpMap),
                        // envMap:window.envMap
                    })
                    // new THREE.MeshBasicMaterial({
                    //     color: new THREE.Color( this.option.worldColor ),
                    //     map: new THREE.TextureLoader().load(this.data.imgMap),
                    //     transparent: true,
                    //     // envMap:window.envMap
                    // })
                );
                if (this.data.imgMap) {
                    this.ground.material = this.createParkMat(new THREE.TextureLoader().load(this.data.imgMap));
                }
                this.ground.receiveShadow = true;
                this.ground.position.set(this.option.worldOffsetX,this.option.worldOffsetY,0);
            }
    
            this.instance.add(this.ground);
            this.option.world.actScene = this;
            new TWEEN.Tween(this.option.world.camera.position).to({x:this.data.camPos[0],y:this.data.camPos[1],z:this.data.camPos[2]},600)
                .start();
            new TWEEN.Tween(this.option.world.controls.target).to({x:this.data.camTar[0],y:this.data.camTar[1],z:this.data.camTar[2]},600)
                .onUpdate(()=>{
                    this.option.world.controls.update();
                })
                .start();
            this.addFallBack();
        }
    
    
        loadGround_svg(data){
            let paths = data.paths;
            let geo = new THREE.Geometry();
            for (let i = 0; i < paths.length; i ++) {
                let path = paths[i];
                let g = this.createGround(path);
                geo.merge(g);
            }
            let material = new THREE.MeshBasicMaterial( {
                color: new THREE.Color( this.option.worldColor ),
                opacity: 0,
                transparent: true,
                side: THREE.DoubleSide
            });
            geo.center();
            let ground = new THREE.Mesh( geo, material );
    
            let floor = new THREE.Mesh(
                new THREE.PlaneGeometry(245,220,1,1),
                new THREE.MeshStandardMaterial({
                    color: new THREE.Color( this.option.groundColor ),
                    transparent: false,
                })
            );
            floor.receiveShadow = true;
            ground.rotateX(Math.PI);
            ground.position.setZ(0.35);
            ground.renderOrder = 1;
            floor.renderOrder = 2;
            this.instance.add( ground );
            this.instance.add(floor);
            this.grounds.push(ground);
            this.effectNodes.push(ground);
            let mirrorGeo = new THREE.BufferGeometry().fromGeometry(geo);
            let points = mirrorGeo.getAttribute("position").array;
            this.computeRange(points);
    
            let scale = this.option.mapWidth / this.width;
            this.instance.scale.set(scale,scale,scale);
            // this.renderParticle({array:points,count:points.length/3,itemSize:3});
            // particleRenderer.particlesNeedUpdate = true;
            TWEEN.removeAll();
            new TWEEN.Tween(this.option.world.camera.position).to({x:this.data.camPos[0],y:this.data.camPos[1],z:this.data.camPos[2]},1000)
                .onUpdate(()=>{
                    this.option.world.controls.target.set(this.data.camTar[0],this.data.camTar[1],this.data.camTar[2]);
                    this.option.world.controls.update();
                })
                // .easing( TWEEN.Easing.Exponential.InOut )
                .start();
            // setTimeout(this.show.bind(this),500);
            this.show(this.option.worldOpacity);
            this.option.world.actScene = this;
    
            this.addFallBack();
        }
    
    
        createGround(path){
            return this.drawRegionStroke(path);
        }
    
        drawRegionStroke(path){
            let geo = new THREE.Geometry();
            for ( let j = 0, jl = path.subPaths.length; j < jl; j ++ ) {
                let subPath = path.subPaths[ j ];
                // path.userData.style.strokeWidth = 0.1;
                let geometry = SVGLoader.pointsToStroke( subPath.getPoints(), path.userData.style );
                if ( geometry ) {
                    geo.merge(new THREE.Geometry().fromBufferGeometry(geometry));
                }
            }
            return geo;
        }
    
        renderParticle(points){
            for (let i = 0; i < points.count; i ++){
    
                if (particleRenderer.particlesData[this.particleInView]){
    
                    particleRenderer.particleTargetPositions[this.particleInView * 3] = (points.array[i * 3]);
                    particleRenderer.particleTargetPositions[this.particleInView * 3 + 1] = points.array[i * 3 + 2] + 3;
                    particleRenderer.particleTargetPositions[this.particleInView * 3 + 2] = (points.array[i * 3 + 1]);
    
                    particleRenderer.particleColors[this.particleInView * 3] = 116 / 255;
                    particleRenderer.particleColors[this.particleInView * 3 + 1] = 160 / 255;
                    particleRenderer.particleColors[this.particleInView * 3 + 2] = 1;
                    let speedRandom = Math.round(Math.random() * 100 / 2) * 2;
                    particleRenderer.particlesData[this.particleInView] = {
                        speed:new THREE.Vector3(
                            (particleRenderer.particleTargetPositions[this.particleInView * 3] - particleRenderer.particlePositions[this.particleInView * 3]) / speedRandom,
                            (particleRenderer.particleTargetPositions[this.particleInView * 3 + 1] - particleRenderer.particlePositions[this.particleInView * 3 + 1]) / speedRandom,
                            (particleRenderer.particleTargetPositions[this.particleInView * 3 + 2] - particleRenderer.particlePositions[this.particleInView * 3 + 2]) / speedRandom),
                        owner:"ground", locked:true};
    
                    this.particleInView ++;
                }
            }
        }
    
        addFallBack() {
            this.fallBack = this.clickOutHandler.bind(this);
            this.option.world.dom.addEventListener(MapEvent.CLICK_OUT,this.fallBack);
            if (this.data.cards){
                this.option.world.bizCardsSys.cardsActive(this.data.cards);
            }
            console.log('this.datathis.data ', this.data)
            this.option.world.dispatchFocusEvent(new CustomEvent(MapEvent.ONFOCUS,{detail:this.data}));
            this.option.world.actScene = this;
        }
    
        removeFallBack(){
            console.log('removeFallBack 111 ' )
            this.option.world.dom.removeEventListener(MapEvent.CLICK_OUT,this.fallBack);
        }
    
        clickOutHandler(evt) {
            console.log(' 44444444444444444 start ');
            this.option.world.actScene = this,
			console.log(' 44444444444444444 end ');
            this.option.world.actScene === this && (this.destroy(),
            setTimeout(( () => {
                /** Liam this.option.world.loadMap()
                */
            }
            ), 0))
        }
    
        redirect(code){
            console.log('redirect 111 ', code )
            for (let build of this.children){
                if (build.code === code){
                    setTimeout(()=>{
                        this.option.world.dispatchExternalEvent(new CustomEvent(MapEvent.NODE_CLICK,{detail: {...build,redirect:true}}));
                    },600);
                }
            }
        }
    
        destroy(){
            this.removeAll();
            // for (let i = 0; i < this.particleInView; i ++){
            //
            //     if (particleRenderer.particlesData[i]){
            //
            //         particleRenderer.particleTargetPositions[i * 3] = 0;
            //         particleRenderer.particleTargetPositions[i * 3 + 1] = 0;
            //         particleRenderer.particleTargetPositions[i * 3 + 2] = 0;
            //
            //         particleRenderer.particleColors[i * 3] = 116 / 255;
            //         particleRenderer.particleColors[i * 3 + 1] = 160 / 255;
            //         particleRenderer.particleColors[i * 3 + 2] = 1;
            //         let speedRandom = Math.round(Math.random() * 100 / 2) * 2;
            //         particleRenderer.particlesData[i] = {
            //             speed:new THREE.Vector3(
            //                 (particleRenderer.particleTargetPositions[i * 3] - particleRenderer.particlePositions[i * 3]) / speedRandom,
            //                 (particleRenderer.particleTargetPositions[i * 3 + 1] - particleRenderer.particlePositions[i * 3 + 1]) / speedRandom,
            //                 (particleRenderer.particleTargetPositions[i * 3 + 2] - particleRenderer.particlePositions[i * 3 + 2]) / speedRandom),
            //             owner:"ground", locked:true};
            //     }
            // }
            scenes.remove(this.instance);
            this.option.world.park = null;
        }

    }
    

    export class Building extends BaseScenes{
        constructor(option,data) {
            data.type = "building";
            super(option,data);
            this.aminateTime = 0;
            if (this.data.model){
                this.loadGlbModel(this.data.model,(model)=>{
                    let m = model.scene;
                    m.rotateX(Math.PI/2);
                    if (this.data.modelScale){
                        m.scale.set(this.data.modelScale,this.data.modelScale,this.data.modelScale);
                    }
                    m.position.setX(this.data.pos[0]);
                    m.position.setY(this.data.pos[1]);
                    this.instance.add(m);
                })
            }else {
                this.createBuilding();
                this.computeRange(this.points.array);
            }
            this.option.world.dom.addEventListener(MapEvent.ONFOCUS, (evt => {
                if (this.tip) {
                    this.tip.marker.visible = evt.detail.type === "park";
                } 
                if (this.box) {
                    this.box.material[1].visible = evt.detail.type === "park";
                    this.box1.material[1].visible = evt.detail.type === "park";
                }
            }));
        }
    
        createBuilding(){
            let paths = SVGs[this.data.svgSrc].paths;
            this.material = new THREE.MeshStandardMaterial({
                    color: this.data.color || this.option.buildingColor,
                    opacity: this.data.opacity || this.option.buildingOpacity,
                    roughness:0.3,
                    depthWrite:false,
                    transparent:true,
                    side:THREE.FrontSide,
                });
    
            let height = this.data.height || 0.1,geos = [] as any[],boxGeos = [] as any[];
            for (let i = 0; i < paths.length; i ++){
                let shapes = paths[i].toShapes(true);
                geos.push(new THREE.ExtrudeGeometry(shapes, {depth: height,bevelEnabled:false}));
                boxGeos.push(...shapes);
            }
            let geo = BufferGeometryUtils.mergeGeometries(geos).center();
            let boxGeo = new THREE.ExtrudeGeometry(boxGeos, {depth: this.data.height,bevelEnabled:false,curveSegments:1}).center()
    
            if (this.data.hasOwnProperty("nodes")){
                setTimeout(this.createFloors.bind(this),0);
            }
            if (this.data.scale){
                geo.scale(this.data.scale[0],this.data.scale[1],1);
            }
            this.points = geo.getAttribute("position");
            let node = new THREE.Mesh(geo,this.material);
            node.position.set(this.data.pos[0],this.data.pos[1],height/2);
            node.name = this.data.name;
            node.code = this.data.code;
            node.scale.set(1,1,-1);
            node.userData = this.data;
            this.building = node;
            this.building.castShadow = true;
            this.building.receiveShadow = true;
            this.instance.add(node);
            this.instance.renderOrder = 3;
        
            this.onclick = this.clickHandler.bind(this);
            this.option.world.dom.addEventListener(MapEvent.NODE_CLICK,this.onclick);
            this.hover = this.overHandler.bind(this);
            this.option.world.dom.addEventListener(MapEvent.MOUSE_OVER,this.hover);
            this.option.world.dom.addEventListener(MapEvent.MOUSE_OVER,this.moveOutHandler.bind(this));
            this.tip = this.createTip();
            if (this.tip){
                this.instance.remove(this.tip);
                this.building.add(this.tip);
            }
            if (this.data.callBack && typeof this.data.callBack === "function"){
                this.data.callBack.call(this,this);
            }
            if (this.option.noInverted)return;
            this.inverted = new THREE.Mesh(geo,this.createInvertedMat(12,this.data.color || this.option.buildingColor,0.01));
            this.inverted.position.set(this.data.pos[0],this.data.pos[1],-height*0.45);
            this.inverted.scale.set(1,1,-0.8);
            this.instance.add(this.inverted);
        }
        
        createFloors(){
            console.log(' Building createFloors 111 ')
            this.nodes = [];
            let len = this.data.nodes.length;
            let pos_y = - this.data.height / 2;
            for (let node of this.data.nodes){
                node.width = this.depth;
                node.height =  node.height || this.data.height / len;//this.option.mapHeight;
                node.depth = this.width;
                node.pos_y = pos_y;
                node.parent = this;
                node.offset = this.data.floorOffset || {x:0,y:0,z:0};
                node.offset_cam = this.data.floorOffset_cam || {x:0,y:0,z:0};
                let floor = new Floor(this.option,node);
                floor.instance.rotateZ(Math.PI/2);
                floor.instance.renderOrder = 10;
                // floor.instance.position.setX(this.mapMaxPoint.x/2);
                // floor.instance.position.setY(this.mapMinPoint.y/2);
                this.building.add(floor.instance);
                this.nodes.push(floor.instance);
                this.children.push(floor);
                pos_y += this.data.height / len;
            }
            this.building.material.visible = false;
            this.building.renderOrder = 9;
            // this.option.world.outlinePass.selectedObjects = [this.instance];
            // this.option.world.outlinePass.selectedObjects = [];
        }
    
        showFloors(exceptCode = null){
            console.log(' Building showFloors 点击楼栋会进入此方法 传入的是楼栋的code ', exceptCode)
            if (!this.data.camPos || !this.data.camTar) {
                return;
            }
            if (this.tip && tthis.tip.marker) {
                this.tip.marker.visible = false;
            }
    
            if (this.box)this.box.material[1].visible = false;
            if (this.box1)this.box1.material[1].visible = false;
            if (exceptCode){
                this.children.forEach((floor,index)=>{
                    if (exceptCode !== floor.code) {
                        floor.show();
                        floor.hideStructure();
                    }
                })
            }else {
                this.option.world.actScene = this;
                let scope = this;
                let duration = 800;
                this.children.forEach((floor,index)=>{
                    console.log(' Building 显示楼层具体数据', index, floor);
                    let ins = floor.instance;
                    floor.show();
                    floor.showStructure();
                    floor.showLabel(true);
                    new TWEEN.Tween(ins.position).to({x:0,y:0,z:- (scope.data.height + (scope.option.mapHeight + scope.option.gap_y)*(scope.children.length-index))},duration)
                        .onStart(()=>{
                            floor.addEventListeners();
                        })
                        .delay(index * duration/2)
                        .easing( TWEEN.Easing.Quadratic.InOut)
                        .start();
                    new TWEEN.Tween(ins.scale).to({x:2,y:2},duration).start();
                });
                new TWEEN.Tween(this.option.world.camera.position).to({x:this.data.camPos[0],y:this.data.camPos[1],z:this.data.camPos[2]},duration)
                    // .onUpdate(()=>{
                    //     this.option.world.controls.target.set(this.data.camTar[0],this.data.camTar[1],this.data.camTar[2]);
                    //     this.option.world.controls.update();
                    // })
                    .onComplete(()=>{
                        this.option.world.nodes = this.nodes;
                        if (this.data.cards)
                            this.option.world.bizCardsSys.cardsActive(this.data.cards);
                    })
                    // .easing( TWEEN.Easing.Exponential.InOut )
                    .start();
                new TWEEN.Tween(this.option.world.controls.target).to({x:this.data.camTar[0],y:this.data.camTar[1],z:this.data.camTar[2]},duration)
                    .onUpdate(()=>{
                        // this.controls.target.set(camTar[0],camTar[1],camTar[2]);
                        this.option.world.controls.update();
                    })
                    .start();
    
                this.addFallBack();
            }
        }
    
        overHandler(evt) {
            console.log(' Building overHandler  111 evt ', evt)
            if (this.data.enable === false || this.option.world.actScene === this) {
                return;
            }
            if (this.checkTarget(evt.detail) === true && this.data.nodes){
                // this.option.world.outlinePass.selectedObjects = [this.instance];
                this.option.world.dom.style.cursor = "pointer";
                // this.box1.material.uniforms.bank = 2.0;
            }
        }
    
        moveOutHandler(evt){
            console.log(' Building moveOutHandler evt ', evt)
            if (this.option.world.actScene === this)return;
            if (this.checkTarget(evt.detail) && this.data.nodes){
                // this.box1.material.uniforms.bank = 3.0;
            }
        }
    
        clickHandler(evt) {
            console.log(' Building 1141-2 clickHandler evt ', evt);
            if (this.data.enable === false || (this.option.world.actScene !== this.parent && !evt.detail.redirect))return;
            if (this.checkTarget(evt.detail) === true && this.data.nodes){
                this.showFloors();
                this.parent.removeFallBack();
            }else {
                if (this.option.world.path && !this.option.world.path.includes(this.code)) {
                    if (this.tip)this.tip.marker.visible = true;
                    this.children.forEach((floor,index)=>{
                        floor.show();
                        floor.showLabel(false);
                    })
                    this.unFocus();
                }
            }
            if (this.tip) {
                let level = evt.detail.redirect ? this.option.world.path.length : this.option.world.getScenePath().length;
                this.tip.marker.visible = level < 2;
            }
        }
    
        unFocus(){
            console.log(' Building unFocus ')
            this.children.forEach((floor,index)=>{
                let ins = floor.instance;
                new TWEEN.Tween(ins.position).to({x:0,y:0,z:- this.data.height / 2 + this.data.height / this.children.length * (index + 0.5) },600)
                    .delay(index * 600/2)
                    .start();
                new TWEEN.Tween(ins.scale).to({x:1,y:1},600).start();
                floor.show();
                floor.hideStructure();
            });
            if (this.box) {
                this.box.material[1].visible = this.option.world.actScene === "park";
                // this.box1.material[1].visible = this.option.world.actScene === "park";
            }
        }
    
        checkTarget(tar){
            console.log(' Building checkTarget tar ', tar)
            if (tar.nodeCode === this.data.code || tar.code === this.data.code)
                return true;
            for (let node of this.nodes){
                if (node.userData.code === tar.nodeCode)
                    return true;
            }
            return false;
        }
    
    
        hideFloors(exceptCode = ""){
            console.log(' Building hideFloors exceptCode ', exceptCode)
            for (let floor of this.children){
                if (floor.code === exceptCode)continue;
                floor.hide();
            }
        }
    
        addFallBack(){
            console.log('addFallBack ')
            this.fallBack = this.clickOutHandler.bind(this);
            this.option.world.dom.addEventListener(MapEvent.CLICK_OUT,this.fallBack);
            this.option.world.dispatchFocusEvent(new CustomEvent(MapEvent.ONFOCUS,{detail:this.data}));
            this.option.world.actScene = this;
        }
    
        removeFallBack(){
            console.log('removeFallBack ')
            this.option.world.dom.removeEventListener(MapEvent.CLICK_OUT,this.fallBack);
        }
    
        removeListeners() {
            console.log('removeListeners ')
            super.removeListeners();
            this.option.world.dom.removeEventListener(MapEvent.NODE_CLICK,this.onclick);
            this.option.world.dom.removeEventListener(MapEvent.MOUSE_OVER,this.hover);
        }
    
        clickOutHandler(evt){
            console.log('clickOutHandler ', evt)
            if (this.option.world.actScene === this){
                let scope = this;
                let duration = 800;
                this.children.forEach((floor,index)=>{
                    let ins = floor.instance;
    
                    new TWEEN.Tween(ins.position).to({z:- scope.data.height / 2 + scope.data.height / scope.children.length * (index + 0.5) },duration)
                        .onStart(()=>{
    
                        })
                        .delay(index * duration/2)
                        .start();
                    new TWEEN.Tween(ins.scale).to({x:1,y:1},duration).start();
                });
                new TWEEN.Tween(this.option.world.camera.position).to({x:this.parent.data.camPos[0],y:this.parent.data.camPos[1],z:this.parent.data.camPos[2]},duration)
                    .onUpdate(()=>{
                        this.option.world.controls.target.set(0,0,0);
                        this.option.world.controls.update();
                    })
                    .onComplete(()=>{
                        this.option.world.nodes = this.parent.buildings;
                    })
                    .start();
                this.option.world.actScene = this.parent;
                if (this.parent.data.cards)
                     this.option.world.bizCardsSys.cardsActive(this.parent.data.cards);
                this.parent.addFallBack();
            }
        }
    
        aminate(){
            this.aminateTime = window.requestAnimationFrame(this.aminate.bind(this));
            if (this.box.material[1].uniforms.time.value <= -this.data.height/2 || this.box.material[1].uniforms.time.value >= this.data.height/2){
                this.aminateValue = -this.aminateValue;
            }
            this.box.material[1].uniforms.time.value += this.aminateValue;
            this.box1.material[1].uniforms.time.value += this.aminateValue;
        }
    }
    
    export class Floor extends BaseScenes{
        constructor(option,data) {
            data.type = "floor";
            super(option,data);
            this.instance.position.z = this.data.pos_y + this.data.height/2;
            if (this.data.isRoof)this.instance.position.z = this.data.parent.data.height / this.data.parent.data.nodes.length + this.data.pos_y - this.data.height/2;
            this.instance.userData = this.data;
            this.loadNodes();
            if (this.data.model)
                this.loadModel();
            this.option.world.dom.addEventListener(MapEvent.ONFOCUS,(evt=>{
                console.log('Floor MapEvent.ONFOCUS ', evt)
                this.floorBox.material.forEach(mat=>{
                    mat.opacity = evt.detail.type === "park" ? this.option.floorOutWallOpacity : this.option.floorOutWallOpacity/10;
                    mat.transparent = evt.detail.type !== "park";
                    // this.labelObj.visible = evt.detail.code === this.data.code || evt.detail.code === this.parent.code;
                })
            }));
        }
    
        loadModel(){
            if (GLB_MODELS[this.data.model]){
                let model = GLB_MODELS[this.data.model];
                if (this.data.modelScale){
                    model.scale.set(this.data.modelScale[0],this.data.modelScale[1],this.data.modelScale[2]);
                }
                this.model = model;
                // this.model.visible = false;
                this.model.rotation.set(-Math.PI/2,0,0);
                // this.instance.add(this.model);
            }
        }
    
        loadNodes(){
            this.structure = this.loadSvg(this.data.svgSrc,this.getRegionColor());
            this.computeRange(this.structure.geometry.getAttribute("position").array);
            this.structure.geometry.scale(this.data.width/this.width,
                this.data.depth/this.depth,
                1
            );
            this.instance.add(this.structure);
    
            if (this.data.isRoof){
                const updateMat = (data)=>{
                    const createMap = (url)=>{
                        let map = new THREE.TextureLoader().load(url);
                        map.format = THREE.RGBAFormat;
                        map.wrapT = map.wrapS = THREE.RepeatWrapping;
                        map.repeat = new THREE.Vector2(2,2);
                        map.anisotropy = 16;
                        map.matrixAutoUpdate = false;
                        if (this.data.scale){
                            if (Array.isArray(this.data.scale[0])){
                                map.matrix.scale(this.data.scale[0][0],this.data.scale[0][1]);
                            }else {
                                map.matrix.scale(this.data.scale[0],this.data.scale[1]);
                            }
                        }
                        if (this.data.mapOffset)
                            map.matrix.translate(this.data.mapOffset[0],this.data.mapOffset[1]);
                        return map;
                    }
                    return [
                        new THREE.MeshBasicMaterial({
                            color: this.option.floorOutWallColor,
                            // needsUpdate:true,
                        }),
                        new THREE.MeshStandardMaterial({
                            color: this.option.floorOutWallColor,
                            opacity: this.option.floorOutWallOpacity,
                            roughness:0.1,
                            transparent:true,
                            // needsUpdate:true,
                            map:createMap(data.map)
                        })];
                }
                if (this.data.nodes){
                    this.data.nodes.forEach(node => {
                        let stuff = this.loadSvg(node.svgSrc,this.option.floorOutWallColor,node.height);
                        stuff.geometry.scale(this.data.width/this.width,
                            this.data.depth/this.depth,
                            1
                        );
                        if (node.map)stuff.material = updateMat(node);
                        if (node.pos) {
                            stuff.position.setX(node.pos[0]);
                            stuff.position.setY(node.pos[1]);
                        }
                        this.instance.add(stuff);
                    })
                }
                this.structure.material = updateMat(this.data);
            }
    
            this.createStage();
            // this.option.world.outlinePass.selectedObjects.push(...this.instance.children);
        }
    
        createRooms(){
            if (this.data.isRoof)return;
            this.nodes = [];
            this.children = [];
            if (this.data.hasOwnProperty("nodes")){
                for (let node of this.data.nodes){
                    node.scale = [this.data.width/this.width,
                        this.data.depth/this.depth,
                        1];
                    node.parent = this;
                    let room = new Room(this.option,node);
                    this.instance.add(room.instance);
                    this.nodes.push(room.instance);
                    this.children.push(room);
                }
            }
            if (this.model) {
                this.instance.add(this.model);
                // this.model.visible = true;
                this.hideStructure();
                this.nodes.push(this.model);
            }
            this.option.world.nodes = this.nodes;
        }
    
    
        checkModel(model){
    
        }
    
    
        removeRooms(){
            if (this.children.length > 0){
                for (let node of this.children){
                    node.removeAll();
                    this.instance.remove(node.instance);
                }
            }
            this.nodes = [];
            this.children = [];
            if (this.model) {
                this.instance.remove(this.model);
                // this.model.visible = false;
                this.showStructure();
            }
        }
    
        loadSvg(svgSrc,color,h){
            let data = SVGs[svgSrc],height = h || this.data.height;
            let paths = data.paths,shapes = [];
    
            const createMap = (url)=>{
                let map = new THREE.TextureLoader().load(url);
                map.format = THREE.RGBAFormat;
                map.anisotropy = 16;
                map.matrixAutoUpdate = false;
                // map.wrapS = THREE.RepeatWrapping;
                // map.wrapT = THREE.RepeatWrapping;
                map.matrix.scale(0.001,0.1);
                map.matrix.translate(0.05,0.9);
                return map;
            }
    
            this.material = [
                new THREE.MeshBasicMaterial({
                    color: this.option.floorWallTopColor,
                    // needsUpdate:true,
                }),
                new THREE.MeshStandardMaterial({
                color: color,
                opacity: this.data.opacity || this.option.wallOpacity,
                roughness:0.3,
                transparent:false,
                // metalness:0.3,
                // needsUpdate:true,
                // map:createMap(this.data.floorWallMap)
                // wireframe:true
            })];
    
            for (let i = 0; i < paths.length; i ++){
                shapes.push(...this.createSolidRegion(paths[i]));
            }
            let geo = new THREE.ExtrudeGeometry(shapes, {depth: height,bevelEnabled:false,curveSegments:1}).center();
            let node = new THREE.Mesh(geo,this.material);
            node.userData = this.data;
            node.name = this.name;
            node.code = this.code;
            node.castShadow = true;
            node.receiveShadow = true;
            node.position.setZ((this.data.height - height)/2);
            node.visible = this.data.isRoof === true;
            // console.log(node.name + ":" +geo.vertices.length);
            // this.instance.add(node);
            return node;
        }
    
        /**
         * 创建单区域网格到map
         * @param path 地区边界描述
         * */
        createSolidRegion(path) {
           //生成切片
            let shapes = path.toShapes(true);
            //获取高度
            // let height = this.option.mapHeight;
            //创建形状
            // let meshGeo = new THREE.ExtrudeGeometry(shapes, {depth: height,bevelEnabled:false,curveSegments:1});
            // meshGeo.scale(0.08,0.08,1);
            return shapes;
        }
    
        createStage(){
            let geo = new THREE.PlaneGeometry(this.data.width,this.data.depth ,1,1),
                boxGeo = new THREE.ExtrudeGeometry(new THREE.Shape(
                        [new  THREE.Vector2( -this.data.width/2, -this.data.depth/2 ),
                            new   THREE.Vector2( -this.data.width/2, this.data.depth/2 ),
                            new   THREE.Vector2( this.data.width/2, this.data.depth/2 ),
                            new   THREE.Vector2( this.data.width/2, -this.data.depth/2 )]),
                    {depth: this.data.height,bevelEnabled:false,curveSegments:1});
                boxGeo.translate(0, 0, -this.data.height/2);
            let mtl = new THREE.MeshStandardMaterial(
                {
                    color: this.data.floorColor || this.option.floorColor,
                    transparent:true,
                    opacity:this.data.floorOpacity || this.option.floorOpacity,
                    side: THREE.DoubleSide,
                    roughness:0.5,
                    // metalness:0.3,
                    // needsUpdate:true,
                    shadowSide:THREE.FrontSide
                }
            );
            if (this.option.floorMap) {
                mtl.map = new THREE.TextureLoader().load(this.option.floorMap);
                mtl.map.anisotropy = 32;
                mtl.map.wrapS = mtl.map.wrapT = THREE.RepeatWrapping;
                mtl.map.repeat.set(20,this.data.depth/this.data.width*20);
            }
    
            if(this.data.groundSvg){
                let shapeGeos = [];
                let boxGeos = [];
                for (let path of SVGs[this.data.groundSvg].paths){
                    shapeGeos.push(new THREE.ShapeGeometry(path.toShapes(true),1));
                    boxGeos.push(...path.toShapes(true));
                }
                geo = BufferGeometryUtils.mergeGeometries(shapeGeos).center();
                geo.computeVertexNormals();
                geo.scale(this.data.width/this.width,
                    this.data.depth/this.depth,
                    1
                );
    
                // mtl.map.matrixAutoUpdate = false;
                // mtl.map.matrix.translate(0.5,0.5);
                // mtl.map.matrix.scale(0.1,0.1);
    
                // boxGeo = BufferGeometryUtils.mergeGeometries(boxGeos).center();
                if (this.data.wallMatchGround !== false){
                    boxGeo = new THREE.ExtrudeGeometry(boxGeos, {depth: this.data.height,bevelEnabled:false,curveSegments:1}).center();
                    boxGeo.scale(this.data.width/this.width,
                        this.data.depth/this.depth,
                        1
                    );
                }
            }
    
            //重新划分groups，支持每个面独立贴图
            if (this.data.maps){
                const uvCount = boxGeo.attributes.uv.count;
                let uvStart = boxGeo.groups[0].start + boxGeo.groups[0].count;
                const firstUv = boxGeo.groups[0];
                boxGeo.groups = [firstUv];
                while (uvStart < uvCount){
                    boxGeo.groups.push( {
                        count:6,
                        materialIndex:boxGeo.groups.length,
                        start:uvStart
                    });
                    uvStart += 6;
                }
            }
    
            const getMap = (url,index)=>{
                let map = new THREE.TextureLoader().load(url);
                map.format = THREE.RGBAFormat;
                map.anisotropy = 16;
                map.matrixAutoUpdate = false;
                // map.mapping = THREE.CubeReflectionMapping;
                // map.rotation = Math.PI / 180 * 90;
                // map.wrapS = map.wrapT = THREE.RepeatWrapping; // CHANGED
                // map.offset.set( 0, 0 ); // CHANGED
                // map.repeat.set( 1.1, 1.1 ); // CHANGED
                if (this.data.scale){
                    if (Array.isArray(this.data.scale[0])){
                        map.matrix.scale(this.data.scale[index][0],this.data.scale[index][1]);
                    }else {
                        map.matrix.scale(this.data.scale[0],this.data.scale[1]);
                    }
                }
    
                if (this.data.mapOffset){
                    if (Array.isArray(this.data.mapOffset[0])){
                        map.matrix.translate(this.data.mapOffset[index][0],this.data.mapOffset[index][1]);
                    }else {
                        map.matrix.translate(this.data.mapOffset[0],this.data.mapOffset[1]);
                    }
                }
                return map;
            }
    
            const createSideMaterials = () => {
                let materials = [];
                const mapsType = this.data.mapsType || "jpg"
                if (this.data.maps){
                    for (let i = 1; i < boxGeo.groups.length; i ++){
                        materials.push(
                            new THREE.MeshStandardMaterial({
                                color: this.option.floorOutWallColor,
                                // emissive:this.option.floorOutWallColor,
                                opacity: this.option.floorOutWallOpacity,
                                roughness:0.3,
                                transparent:true,
                                side:THREE.DoubleSide,
                                // needsUpdate:true,
                                map:getMap(this.data.maps + "/" + i + "." + mapsType,i-1),
                                // normalMap:getMap(this.data.maps + "/" + i + "_normal." + mapsType, i-1),
                                // bumpMap:getMap(this.data.maps + "/" + i + "_bump." + mapsType, i-1)
                            })
                        )
                    }
                }else {
                    materials = [
                        new THREE.MeshStandardMaterial({
                            color:this.option.floorOutWallColor,
                            // emissive:this.option.floorOutWallColor,
                            opacity: this.option.floorOutWallOpacity,
                            roughness:0.3,
                            transparent:true,
                            // depthWrite:false,
                            // deepTest:false,
                            side:THREE.DoubleSide,
                            // needsUpdate:true
                        })
                    ]
                }
                return materials;
            }
            // this.computeVectors(boxGeo);
            // let floorBox = new THREE.Mesh( boxGeo, new EdgeMaterial({color:"#200359",opacity:this.option.floorOutWallOpacity}));
            this.floorBox = new THREE.Mesh( boxGeo, [
                new THREE.MeshStandardMaterial({
                    color:  this.option.floorOutTopColor || this.option.floorOutWallColor,
                    // emissive:this.option.floorOutWallColor,
                    opacity: this.option.floorOutWallOpacity,
                    roughness:0.5,
                    transparent:true,
                    // metalness:0.1,
                    side:THREE.FrontSide,
                    // envMap:window.envMap,
                    // needsUpdate:true,
                    visible:!this.data.isRoof
                    // wireframe:true
                }),
                ...createSideMaterials()
                // this.createFloorWallMat(this.data.height,"#015d55",0.7)
                // new THREE.MeshBasicMaterial({color:"#0f153d",transparent:true,opacity:0.4,side:THREE.DoubleSide,depthWrite:false,depthTest:false})
            ]);
            // let floorBox = new THREE.Mesh( boxGeo, this.createFloorWallMat());
    
            this.floorBox.renderOrder = 5;
            this.floorBox.code = this.code;
            this.floorBox.receiveShadow = true;
            this.floorBox.castShadow = true;
            if (!this.data.isRoof || this.data.showBox)
                this.instance.add(this.floorBox);
            this.ground = new THREE.Mesh( geo, mtl );
            this.ground.receiveShadow = false;
            this.ground.castShadow = false;
            // this.ground.rotateX( Math.PI / 2 );
            this.ground.position.set(0,0,this.data.height/2);
            this.ground.name = this.name;
            this.ground.code = this.code;
            this.ground.userData = this.data;
            this.ground.renderOrder = 3;
            this.ground.visible = false;
            this.instance.add( this.ground );
    
            console.log('Floor inverted 11 ', this.name)
            if (!this.inverted) {
                this.createLabel(this.name);
            }
        }
    
    
        createLabel(label) {
            
            console.log(' 创建 楼层 Label createLabel  ', label)

            let floorLabel = document.createElement("div");
            floorLabel.className = "floorLabel";
            floorLabel.innerHTML = label;
            this.labelObj = new CSS3DObject(floorLabel);
            this.labelObj.rotateX(-Math.PI/2);
            this.labelObj.rotateY(-Math.PI/2);
            this.labelObj.position.set(this.data.width/2,0,0.1);
            this.labelObj.scale.multiplyScalar(this.option.floorLabelScale || 0.05);
            this.labelObj.visible = false;
            this.instance.add(this.labelObj);
        }
    
        showLabel(show){
            this.labelObj.visible = show;
        }
    
        addEventListeners() {
            console.log('Floor addEventListeners ')
            this.hover = this.highLight.bind(this);
            this.option.world.dom.addEventListener(MapEvent.MOUSE_OVER,this.hover);
            this.onout = this.highLight.bind(this);
            this.option.world.dom.addEventListener(MapEvent.MOUSE_OUT,this.onout);
            this.onclick = this.clickHandler.bind(this);
            this.option.world.dom.addEventListener(MapEvent.NODE_CLICK,this.onclick);
            this.onclickout = this.clickOutHandler.bind(this);
            this.option.world.dom.addEventListener(MapEvent.CLICK_OUT,this.onclickout);
        }
    
        removeListeners() {
            super.removeListeners();
            this.option.world.dom.removeEventListener(MapEvent.MOUSE_OVER,this.hover);
            this.option.world.dom.removeEventListener(MapEvent.MOUSE_OUT,this.onout);
            this.option.world.dom.removeEventListener(MapEvent.NODE_CLICK,this.onclick);
            this.option.world.dom.removeEventListener(MapEvent.CLICK_OUT,this.onclickout);
        }
    
        clickHandler(evt){
            console.log(' Floor clickHandler(t)', evt);
            // if (this.option.world.actScene !== this.parent)return;
            this.data.isRoof || evt.detail.redirect && this.option.world.path.length < 3 || evt.detail.code === this.code && (this.option.world.actScene === this || (this.beFocus(),
            this.parent.hideFloors(this.code),
            this.parent.removeFallBack()))
        }
    
        clickOutHandler(evt){
            evt.stopPropagation();
            if (this.option.world.actScene === this){
                this.unFocus();
                this.parent.showFloors(this.code);
            }
        }
    
        highLight(event){
            console.log('Floor highLight ', event)
            if (this.option.world.actScene instanceof Floor)return;
            if (this.code === event.detail.nodeCode){
    
            }
            // for (let child of this.instance.children){
            //     if (child.material){
            //         if (this.code === event.detail.nodeCode){
            //             let type = child.geometry.type;
            //             if (type === "Geometry"){
            //                 child.material.opacity = this.data.wallOpacity || this.option.wallOpacity;
            //             }else if (type === "PlaneGeometry"){
            //                 child.material.opacity = this.data.floorOpacity || this.option.floorOpacity;
            //             }else{
            //                 // child.material.opacity = 0.1;
            //             }
            //             child.material.depthWrite = true;
            //             this.option.world.dom.style.cursor = "pointer";
            //         }else {
            //             child.material.opacity = this.data.ignoreOpacity || this.option.ignoreOpacity;
            //             child.material.depthWrite = true;
            //         }
            //     }
            // }
        }
    
        show(){
            console.log('点击楼栋，显示楼层时会调用此方法，去掉房间细节，只显示楼层墙体 Floor show ')
            this.removeRooms();
            for (let child of this.instance.children){
                if (child.material){
                //     child.material.opacity = this.data.ignoreOpacity || this.option.ignoreOpacity;
                //     new TWEEN.Tween(child.material).to({opacity:0.1},50).onComplete(()=>{
                        if ( Array.isArray( child.material ) ){
                            child.material.forEach(mat => {
                                mat.visible = true;
                            })
                        }
                        child.material.visible = true;
                        child.material.depthWrite = true;
                        // let type = child.geometry.type;
                        // if (type === "BufferGeometry"){
                        //     child.material.opacity = this.data.wallOpacity || this.option.wallOpacity;
                        // }else if (type === "PlaneGeometry"){
                        //     child.material.opacity = this.data.floorOpacity || this.option.floorOpacity;
                        // }else{
                        //     // child.material.opacity = 0.1;
                        // }
                //     })
                //         // .repeat(Math.random()*10/2)
                //         // .repeatDelay(Math.random()*100)
                //         // .delay(600)
                //         .onStart(()=>{this.visible = true;})
                //         .start();
                }else {
                    child.visible = true;
                }
                child.castShadow = true;
            }
            this.ground.receiveShadow = false;
        }
    
        hide(){
            console.log('Floor hide ')
            const doHide = ()=>{
                for (let child of this.instance.children){
                    if (child.material){
                        // child.material.opacity = 0;
                        // child.material.depthWrite = false;
                        if ( Array.isArray( child.material ) ){
                            child.material.forEach(mat => {
                                mat.visible = false;
                            })
                        }
                        child.material.visible = false;
                    }else {
                        child.visible = false;
                    }
                    child.castShadow = false;
                }
                this.visible = false;
                this.removeRooms();
            }
    
            new TWEEN.Tween(this.instance.position)
                .to({x:0,y:0},600)
                .onComplete(()=>{
                    doHide();
                })
                .start();
        }
    
        beFocus(){
            console.log(' Floor beFocus() this.show() 1111 ');
            this.show();
            this.option.world.actScene = this;
            let worldPos = new THREE.Vector3();
            let tw_cam = new TWEEN.Tween(this.option.world.camera.position)
                .onUpdate(()=>{
                    this.option.world.controls.target.set(worldPos.x,worldPos.y,worldPos.z);
                    this.option.world.controls.update();
                })
                // .easing( TWEEN.Easing.Exponential.InOut )
                .onComplete(()=>{
                    this.createRooms();
                    this.ground.receiveShadow = true;
                });
            new TWEEN.Tween(this.instance.position)
                .to({x:this.instance.position.x + this.data.offset.x,
                    y:this.instance.position.y + this.data.offset.y,
                    z:this.instance.position.z + this.data.offset.z},600)
                .onComplete(()=>{
                    this.instance.getWorldPosition(worldPos);
                    tw_cam.to({x:worldPos.x + this.data.offset_cam.x,
                        y:worldPos.y + this.data.offset_cam.y,
                        z:worldPos.z + this.data.offset_cam.z},600).start();
                })
                // .easing( TWEEN.Easing.Exponential.InOut )
                .start();
    
            // this.createRooms();
            this.option.world.dispatchFocusEvent(new CustomEvent(MapEvent.ONFOCUS, { detail: this.data }));
            console.log(' Floor beFocus() this.show() 2222 showStructure ');
            this.option.world.actScene = this;
            this.showStructure();
            if (this.data.hasOwnProperty("cards")){
                // this.option.world.bizCardsSys.cardsActive(this.data.cards);
            }
        }
    
        unFocus(){
            console.log('Floor unFocus ')
            this.ground.receiveShadow = false;
            this.option.world.actScene = this.parent;
            let tw_cam = new TWEEN.Tween(this.option.world.camera.position)
                .to({x:this.parent.data.camPos[0],y:this.parent.data.camPos[1],z:this.parent.data.camPos[2]},600)
                .onUpdate(()=>{
                    this.option.world.controls.target.set(this.parent.data.camTar[0],this.parent.data.camTar[1],this.parent.data.camTar[2]);
                    this.option.world.controls.update();
                })
                // .easing( TWEEN.Easing.Exponential.InOut )
                .onComplete(()=>{
                    // this.labelObj.rotation.x  = Math.PI/2;
                    this.removeRooms();
                    this.parent.addFallBack();
                });
            new TWEEN.Tween(this.instance.position)
                .to({x:0},600)
                .onComplete(()=>{
                    tw_cam.start();
                })
                // .easing( TWEEN.Easing.Exponential.InOut )
                .start();
            // this.removeRooms();
            if (this.parent.data.hasOwnProperty("cards")){
                // this.option.world.bizCardsSys.cardsActive(this.parent.data.cards);
            }
        }
    
        hideStructure(){
            console.log('Floor hideStructure ')
            if (this.structure)this.structure.visible = false;
            this.floorBox.visible = false;
            this.ground.visible = false;
            this.ground.receiveShadow = false;
            this.labelObj.visible = false;
    
        }
    
        showStructure(){
            console.log('点击楼栋，显示楼层时会调用此方法 Floor showStructure ')
            if (this.structure)this.structure.visible = true;
            this.floorBox.visible = false;
            this.ground.visible = true;
    
        }
    
        getRegionColor(){
            console.log('Floor getRegionColor ')
            return new THREE.Color(this.data.wallColor || this.option.wallColor);
        }    
    
    }
    
    export class Room extends BaseScenes{
        constructor(option,data) {
            data.type = "room";
            super(option,data);
            this.parent = data.parent;
            this.instance.userData = data;
            if (this.option.roomDataFn && this.data.code){
                this.loadRealRoom()
            }else {
                this.createRoom();
            }
            this.createLabel();
        }
    
        createRoom(){
            console.log('Room createRoom 111')
            if (!this.data.svgSrc) {
                return;
            }
            let node = this.loadSvg(this.data.svgSrc,this.data.color || this.option.roomColor);
            node.geometry.scale(this.data.scale[0],this.data.scale[1],this.data.scale[2]);
            node.position.set(this.data.pos[0],this.data.pos[1],(this.option.mapHeight-this.option.roomHeight)/2);
            node.userData = this.data;
            node.code = this.code;
            if (this.code.length === 0) {
                return;
            }
            console.log('Room createRoom 222 ', this.code)
            this.hover = this.overHandler.bind(this);
            this.option.world.dom.addEventListener(MapEvent.MOUSE_OVER,this.hover);
            this.onclick = this.clickHandler.bind(this);
            this.option.world.dom.addEventListener(MapEvent.NODE_CLICK,this.onclick);
        }
    
        overHandler(evt) {
            console.log('Room overHandler ', evt)
            console.log('Room overHandler this.option.world.actScene ', this.option.world.actScene)
            console.log('Room overHandler this.parent ', this.parent)
            if (evt.detail.nodeCode === this.code && this.option.world.actScene === this.parent){
                this.option.world.dom.style.cursor = "pointer";
            }
        }
    
        clickHandler(evt){
            console.log('Room clickHandler ', evt)
            this.option.world.actScene !== this.parent && this.option.world.actScene.parent !== this.parent || evt.detail.code === this.code && (this.option.world.actScene = this,
            this.option.world.dispatchFocusEvent(new CustomEvent("onFocus",{
                detail: this.data
            })))

        }
    
        removeListeners() {
            console.log('Room removeListeners ')
            super.removeListeners();
            this.option.world.dom.removeEventListener(MapEvent.MOUSE_OVER,this.hover);
            this.option.world.dom.removeEventListener(MapEvent.NODE_CLICK,this.onclick);
        }
    
        loadSvg(svgSrc,color){
            let data = SVGs[svgSrc];
            let paths = data.paths,geos = [];
            this.material = new THREE.MeshStandardMaterial({
                color: color,
                opacity: this.data.opacity || this.option.roomOpacity,
                transparent: true,
                depthWrite:true,
                depthTest:true
            });
            for (let i = 0; i < paths.length; i ++){
                geos.push(this.createSolidRegion(paths[i]));
            }
            let racksGeo = BufferGeometryUtils.mergeGeometries(geos).center();
            racksGeo.computeBoundingBox();
            let node = new THREE.Mesh(racksGeo,this.material);
            node.name = this.name;
            node.userData = this.data;
            // this.floor = node;
            node.castShadow = true;
    
            let roomGeo = new THREE.BoxGeometry(racksGeo.boundingBox.max.x * 2 + 4,racksGeo.boundingBox.max.y * 2 + 4,racksGeo.boundingBox.max.z,1,1,1);
            roomGeo.scale(this.data.scale[0],this.data.scale[1],this.data.scale[2]);
            let floorMat = new THREE.MeshBasicMaterial({
                color:color,
                opacity:0,
                depthWrite:false
            })
            let room = new THREE.Mesh(roomGeo,floorMat);
            room.userData = this.data;
            room.code = this.code;
            node.add(room);
            this.instance.add(node);
            return node;
        }
    
        createSolidRegion(path) {
            let shapes = path.toShapes(true);
            let height = this.option.roomHeight || this.option.mapHeight;
            let meshGeo = new THREE.ExtrudeGeometry(shapes, {depth: height,bevelEnabled:false,curveSegments:1});
            return meshGeo;
        }
    
        createLabel() {
            console.log(' 创建房间Label createLabel  ')
            let floorLabel = document.createElement("div");
            floorLabel.className = this.data.className || "roomLabel";
            floorLabel.innerHTML = this.name;
            floorLabel.onclick = (evt)=>{
                console.log('Room floorLabel onclick ', evt)
                this.option.world.dispatchExternalEvent(new CustomEvent(MapEvent.NODE_CLICK, {detail:this.data}));
            };
    
            floorLabel.onmouseover = (evt)=>{
                console.log('Room floorLabel onmouseover ', evt)
                if (this.code.length > 0)
                    this.option.world.dom.style.cursor = "pointer";
                else
                    this.option.world.dom.style.cursor = "default";
            };
    
            let labelObj = new CSS3DObject(floorLabel);
            labelObj.rotateX(Math.PI);
            labelObj.rotateY(Math.PI);
            let deg = this.data.hasOwnProperty('labelRotate') ? Math.PI * (this.data.labelRotate / 180) : Math.PI;
            labelObj.rotateZ(deg);
            labelObj.scale.multiplyScalar(this.option.roomLabelScale || 0.05);
            let label_x = this.data.pos[0],label_y = this.data.pos[1];
            if (this.data.hasOwnProperty("labelPos")){
                label_x += this.data.labelPos[0];
                label_y += this.data.labelPos[1];
            }
            labelObj.position.set(label_x,label_y,-this.option.mapHeight/2);
            this.instance.add(labelObj);
            // this.billBoards.push(labelObj);
        }
    //**************************************************V3.0 add****************************************************//
    //************************************获取机房台账数据，绘制真实机房场景**********************************************//
    
        loadRealRoom(){
            this.createRoom();
            // this.roomData = this.option.roomDataFn.call(this,this.data.code).content;
            // if (!this.roomData) {
            //     this.createRoom();
            //     return;
            // }
            // for (let data in this.roomData){
            //     this.createNode(data);
            // }
        }
    
        createNode(data){
            let node = new Node(this.option, data);
            this.instance.add(node);
        }
    }
    
    
export class Node extends BaseScenes{
    constructor(option,data) {
        data.type = "node";
        super(option,data);
        this.parent = data.parent;
        this.instance.userData = data;
        this.createBox();
    }

    createBox(){
        this.boxGeo = THREE.BoxGeometry(this.data.width, this.data.height, this.data.depth);
    }
}
 
}



